Showing preview only (634K chars total). Download the full file or copy to clipboard to get everything.
Repository: yeasy/docker_practice
Branch: master
Commit: 0648f6397914
Files: 265
Total size: 576.9 KB
Directory structure:
gitextract_132u_zi8/
├── .devcontainer/
│ └── devcontainer.json
├── .docker/
│ └── docker-entrypoint.sh
├── .editorconfig
├── .gitattributes
├── .github/
│ ├── CODEOWNERS
│ ├── FUNDING.yml
│ ├── ISSUE_TEMPLATE/
│ │ ├── Bug_report.md
│ │ ├── Custom.md
│ │ └── Feature_request.md
│ ├── PULL_REQUEST_TEMPLATE.md
│ ├── dependabot.yml
│ └── workflows/
│ ├── check-link.yml
│ ├── ci.yaml
│ ├── dependabot-automerge.yml
│ └── release-pdf.yml
├── .gitignore
├── .vuepress/
│ ├── .gitignore
│ └── config.js
├── .zhlintignore
├── 01_introduction/
│ ├── 1.1_quickstart.md
│ ├── 1.2_what.md
│ ├── 1.3_why.md
│ ├── README.md
│ └── summary.md
├── 02_basic_concept/
│ ├── 2.1_image.md
│ ├── 2.2_container.md
│ ├── 2.3_repository.md
│ ├── README.md
│ └── summary.md
├── 03_install/
│ ├── 3.10_experimental.md
│ ├── 3.1_ubuntu.md
│ ├── 3.2_debian.md
│ ├── 3.3_fedora.md
│ ├── 3.4_centos.md
│ ├── 3.5_raspberry-pi.md
│ ├── 3.6_offline.md
│ ├── 3.7_mac.md
│ ├── 3.8_windows.md
│ ├── 3.9_mirror.md
│ ├── README.md
│ └── summary.md
├── 04_image/
│ ├── 4.1_pull.md
│ ├── 4.2_list.md
│ ├── 4.3_rm.md
│ ├── 4.4_commit.md
│ ├── 4.5_build.md
│ ├── 4.6_other.md
│ ├── 4.7_internal.md
│ ├── README.md
│ ├── demo/
│ │ ├── buildkit/
│ │ │ ├── Dockerfile
│ │ │ ├── Dockerfile.buildkit
│ │ │ ├── aws.txt
│ │ │ ├── package.json
│ │ │ └── src/
│ │ │ └── index.js
│ │ ├── multi-arch/
│ │ │ └── Dockerfile
│ │ └── multistage-builds/
│ │ ├── .gitignore
│ │ ├── Dockerfile
│ │ ├── Dockerfile.build
│ │ ├── Dockerfile.copy
│ │ ├── Dockerfile.one
│ │ ├── app.go
│ │ └── build.sh
│ └── summary.md
├── 05_container/
│ ├── 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
│ ├── README.md
│ └── summary.md
├── 06_repository/
│ ├── 6.1_dockerhub.md
│ ├── 6.2_registry.md
│ ├── 6.3_registry_auth.md
│ ├── 6.4_nexus3_registry.md
│ ├── README.md
│ ├── demo/
│ │ ├── auth/
│ │ │ └── nginx.htpasswd
│ │ ├── config.yml
│ │ ├── docker-compose.yml
│ │ ├── root-ca.cnf
│ │ ├── site.cnf
│ │ └── ssl/
│ │ ├── docker.domain.com.crt
│ │ └── docker.domain.com.key
│ └── summary.md
├── 07_dockerfile/
│ ├── 7.10_workdir.md
│ ├── 7.11_user.md
│ ├── 7.12_healthcheck.md
│ ├── 7.13_onbuild.md
│ ├── 7.14_label.md
│ ├── 7.15_shell.md
│ ├── 7.16_references.md
│ ├── 7.17_multistage_builds.md
│ ├── 7.18_multistage_builds_laravel.md
│ ├── 7.1_run.md
│ ├── 7.2_copy.md
│ ├── 7.3_add.md
│ ├── 7.4_cmd.md
│ ├── 7.5_entrypoint.md
│ ├── 7.6_env.md
│ ├── 7.7_arg.md
│ ├── 7.8_volume.md
│ ├── 7.9_expose.md
│ ├── README.md
│ ├── multistage_example/
│ │ └── laravel/
│ │ ├── .dockerignore
│ │ ├── Dockerfile
│ │ └── laravel.conf
│ └── summary.md
├── 08_data/
│ ├── 8.1_volume.md
│ ├── 8.2_bind-mounts.md
│ ├── 8.3_tmpfs.md
│ ├── README.md
│ └── summary.md
├── 09_network/
│ ├── 9.1_dns.md
│ ├── 9.2_network_types.md
│ ├── 9.3_custom_network.md
│ ├── 9.4_container_linking.md
│ ├── 9.5_port_mapping.md
│ ├── 9.6_network_isolation.md
│ ├── 9.7_advanced_networking.md
│ ├── README.md
│ └── summary.md
├── 10_buildx/
│ ├── 10.1_buildkit.md
│ ├── 10.2_buildx.md
│ ├── 10.3_multi-arch-images.md
│ ├── README.md
│ └── summary.md
├── 11_compose/
│ ├── 11.1_introduction.md
│ ├── 11.2_install.md
│ ├── 11.3_usage.md
│ ├── 11.4_commands.md
│ ├── 11.5_compose_file.md
│ ├── 11.6_django.md
│ ├── 11.7_rails.md
│ ├── 11.8_wordpress.md
│ ├── 11.9_lnmp.md
│ ├── README.md
│ ├── demo/
│ │ ├── app/
│ │ │ ├── Dockerfile
│ │ │ ├── app.py
│ │ │ └── docker-compose.yml
│ │ ├── django/
│ │ │ ├── .gitignore
│ │ │ ├── Dockerfile
│ │ │ ├── docker-compose.yml
│ │ │ └── requirements.txt
│ │ └── wordpress/
│ │ └── docker-compose.yml
│ └── summary.md
├── 12_implementation/
│ ├── 12.1_arch.md
│ ├── 12.2_namespace.md
│ ├── 12.3_cgroups.md
│ ├── 12.4_ufs.md
│ ├── 12.5_container_format.md
│ ├── 12.6_network.md
│ ├── README.md
│ └── summary.md
├── 13_kubernetes_concepts/
│ ├── 13.1_intro.md
│ ├── 13.2_concepts.md
│ ├── 13.3_design.md
│ ├── 13.4_advanced.md
│ ├── 13.5_practice.md
│ ├── README.md
│ └── summary.md
├── 14_kubernetes_setup/
│ ├── 14.1_kubeadm.md
│ ├── 14.2_kubeadm-docker.md
│ ├── 14.3_docker-desktop.md
│ ├── 14.4_kind.md
│ ├── 14.5_k3s.md
│ ├── 14.6_systemd.md
│ ├── 14.7_dashboard.md
│ ├── 14.8_kubectl.md
│ ├── README.md
│ └── summary.md
├── 15_etcd/
│ ├── 15.1_intro.md
│ ├── 15.2_install.md
│ ├── 15.3_cluster.md
│ ├── 15.4_etcdctl.md
│ ├── README.md
│ ├── demo/
│ │ └── cluster/
│ │ └── docker-compose.yml
│ └── summary.md
├── 16_cloud/
│ ├── 16.1_intro.md
│ ├── 16.2_tencentCloud.md
│ ├── 16.3_alicloud.md
│ ├── 16.4_aws.md
│ ├── 16.5_multicloud.md
│ ├── README.md
│ └── summary.md
├── 17_ecosystem/
│ ├── 17.1_coreos_intro.md
│ ├── 17.2_coreos_install.md
│ ├── 17.3_podman.md
│ ├── 17.4_buildah.md
│ ├── 17.5_skopeo.md
│ ├── 17.6_containerd.md
│ ├── 17.7_secure_runtime.md
│ ├── 17.8_wasm.md
│ ├── README.md
│ ├── coreos_README.md
│ ├── demo/
│ │ └── example.fcc
│ └── summary.md
├── 18_security/
│ ├── 18.1_kernel_ns.md
│ ├── 18.2_control_group.md
│ ├── 18.3_daemon_sec.md
│ ├── 18.4_kernel_capability.md
│ ├── 18.5_other_feature.md
│ ├── 18.6_image_security.md
│ ├── README.md
│ └── summary.md
├── 19_observability/
│ ├── 19.1_prometheus.md
│ ├── 19.2_elk.md
│ ├── 19.3_performance_optimization.md
│ ├── README.md
│ └── summary.md
├── 20_cases_os/
│ ├── 20.1_busybox.md
│ ├── 20.2_alpine.md
│ ├── 20.3_debian.md
│ ├── 20.4_centos.md
│ ├── README.md
│ └── summary.md
├── 21_case_devops/
│ ├── 21.1_devops_workflow.md
│ ├── 21.2_github_actions.md
│ ├── 21.3_drone.md
│ ├── 21.4_drone_demo.md
│ ├── 21.5_ide.md
│ ├── 21.6_vsCode.md
│ ├── 21.7_practical_examples.md
│ ├── README.md
│ ├── drone_demo.app.go
│ ├── drone_demo.drone.yml
│ ├── drone_docker-compose.yml
│ ├── drone_env.example
│ ├── drone_gitignore
│ └── summary.md
├── CHANGELOG.md
├── CONTRIBUTING.md
├── README.md
├── SUMMARY.md
├── _config.yml
├── _images/
│ └── cover.sketch
├── appendix/
│ ├── README.md
│ ├── _images/
│ │ ├── cmd_logic 2.graffle/
│ │ │ └── data.plist
│ │ ├── cmd_logic.dot
│ │ ├── cmd_logic.dot.bak
│ │ ├── cmd_logic.graffle/
│ │ │ └── data.plist
│ │ └── container_status.dot
│ ├── best_practices.md
│ ├── command/
│ │ ├── README.md
│ │ ├── docker.md
│ │ └── dockerd.md
│ ├── debug.md
│ ├── faq/
│ │ ├── README.md
│ │ └── errors.md
│ ├── glossary.md
│ ├── learning_roadmap.md
│ ├── repo/
│ │ ├── README.md
│ │ ├── centos.md
│ │ ├── minio.md
│ │ ├── mongodb.md
│ │ ├── mysql.md
│ │ ├── nginx.md
│ │ ├── nodejs.md
│ │ ├── php.md
│ │ ├── redis.md
│ │ ├── ubuntu.md
│ │ └── wordpress.md
│ └── resources.md
├── book.json
├── docker-compose.yml
└── package.json
================================================
FILE CONTENTS
================================================
================================================
FILE: .devcontainer/devcontainer.json
================================================
// https://code.visualstudio.com/docs/remote/devcontainerjson-reference
{
"image": "yeasy/docker_practice:latest",
"mounts": [
"source=dp-code-remote-cache,target=/root/.vscode-server,type=volume"
],
"settings": {
"terminal.integrated.shell.linux": "/bin/sh"
},
"forwardPorts": [
4000
],
"runArgs": [
"--cap-add=SYS_ADMIN"
],
"postStartCommand": [
"sh",
"-cx",
"pwd ; cd /workspaces/docker_practice ; mkdir -p ${PWD}/node_modules; mkdir -p ${PWD}/_book; mount --bind /srv/gitbook/node_modules ${PWD}/node_modules ; mount --bind /mnt ${PWD}/_book"
]
}
================================================
FILE: .docker/docker-entrypoint.sh
================================================
#!/usr/bin/env sh
echo
echo
echo "Please open your browser: 127.0.0.1:4000"
echo
echo "欢迎加入 QQ 群:【 145983035 】 分享 Docker 资源,交流 Docker 技术"
echo
echo
exec nginx -g "daemon off;"
================================================
FILE: .editorconfig
================================================
# EditorConfig is awesome: https://EditorConfig.org
root = true
[*]
indent_style = space
indent_size = 2
end_of_line = lf
charset = utf-8
trim_trailing_whitespace = true
insert_final_newline = true
[*.md]
trim_trailing_whitespace = false
[*.py]
indent_size = 4
[Makefile]
indent_style = tab
================================================
FILE: .gitattributes
================================================
* text=auto eol=lf
*.sh text eol=lf
* linguist-language=go
================================================
FILE: .github/CODEOWNERS
================================================
* @yeasy @khs1994
/.github/* @khs1994
/.travis/* @khs1994
/.vuepress/* @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
/.drone.yml @khs1994
/.editorconfig/ @khs1994
/.gitattributes @khs1994
/.gitignore @khs1994
/_config.yml @yeasy @khs1994
/book.json @yeasy @khs1994
/CHANGELOG.md @yeasy @khs1994
/CONTRIBUTING.md @yeasy @khs1994
/docker-compose.yml @khs1994
/manifest @khs1994
/package.json @khs1994
/README.md @yeasy @khs1994
/SUMMARY.md @yeasy @khs1994
================================================
FILE: .github/FUNDING.yml
================================================
# These are supported funding model platforms
github: yeasy
patreon: # Replace with a single Patreon username
open_collective: # Replace with a single Open Collective username
ko_fi: # Replace with a single Ko-fi username
tidelift: # Replace with a single Tidelift platform-name/package-name e.g., npm/babel
community_bridge: # Replace with a single Community Bridge project-name e.g., cloud-foundry
liberapay: # Replace with a single Liberapay username
issuehunt: # Replace with a single IssueHunt username
otechie: # Replace with a single Otechie username
lfx_crowdfunding: # Replace with a single LFX Crowdfunding project-name e.g., cloud-foundry
custom: # Replace with up to 4 custom sponsorship URLs e.g., ['link1', 'link2']
================================================
FILE: .github/ISSUE_TEMPLATE/Bug_report.md
================================================
---
name: Bug report
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-->
* [x] Linux
* [x] CentOS 7
* [x] Fedora
* [x] Ubuntu 16.04 +
* [x] Debian 9 +
* [x] macOS
* [x] Windows 10
* [x] Raspberry Pi (ARM)
* [x] Others (Pls describe below)
### Docker Version
<!--如果你的 Docker 版本低于 20.10 请尽可能升级到该版本,保留你的 Docker 版本,其他选项删除-->
<!--if Docker version under 20.10, please upgrade Docker to 20.10-->
* [x] Test (v20.10)
* [x] Stable (v20.10)
* [x] 1.13.0 or Before
### Problem Description
<!--描述你的问题,请贴出操作步骤,终端报错截图或文字信息-->
<!--describe problem with detailed steps and logs-->
<!--提交问题之前请点击预览标签,符合要求之后再提交问题-->
================================================
FILE: .github/ISSUE_TEMPLATE/Custom.md
================================================
---
name: Custom issue template
about: Create a issue about Docker
---
* [ ] Have u googled the problem? If no, pls do that first!
### Environment
<!--请提供环境信息,包括操作系统版本等,保留你的操作系统,其他选项删除-->
<!--Provides env info like OS version-->
* [x] Linux
* [x] CentOS 7
* [x] Fedora
* [x] Ubuntu 16.04 +
* [x] Debian 9 +
* [x] macOS
* [x] Windows 10
* [x] Raspberry Pi (ARM)
* [x] Others (Pls describe below)
### Docker Version
<!--如果你的 Docker 版本低于 20.10 请尽可能升级到该版本,保留你的 Docker 版本,其他选项删除-->
<!--if Docker version under 20.10, please upgrade Docker to 20.10-->
* [x] Test (v20.10)
* [x] Stable (v20.10)
* [x] 1.13.0 or Before
### Problem Description
<!--描述你的问题,请贴出操作步骤,终端报错截图或文字信息-->
<!--describe problem with detailed steps and logs-->
<!--提交问题之前请点击预览标签,符合要求之后再提交问题-->
================================================
FILE: .github/ISSUE_TEMPLATE/Feature_request.md
================================================
---
name: Feature request
about: Suggest an idea for docker_practice
---
================================================
FILE: .github/PULL_REQUEST_TEMPLATE.md
================================================
<!--
Thanks for your contribution.
See [CONTRIBUTING](../CONTRIBUTING.md) for contribution guidelines.
-->
**Proposed changes (Mandatory)**
<!--
Tell us what you did and why:
One line short description
And details in other paragraphs.
-->
**Fix issues (Optional)**
<!--
Tell us what issues you fixed, e.g., fix #123
-->
================================================
FILE: .github/dependabot.yml
================================================
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:
- "*"
================================================
FILE: .github/workflows/check-link.yml
================================================
name: Check link
on:
workflow_dispatch:
jobs:
check-link:
name: check-link
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
# search Issues :-(
- run: |
docker run -i --rm \
-v $PWD:/mnt:ro \
dkhamsing/awesome_bot \
--white-list "localhost","0.0.0.0",\
"server","example.com","docker",\
"docker.domain.com","YourIP","register",\
"172.16.238.100","172.16.238.101","172.16.238.102",\
"192.168.199.100",\
"github.com/settings",\
"github.com/docker/compose/releases/download",\
"github.com/etcd-io/etcd/releases/download",\
"github.com/tianon/gosu/releases/download",\
"github.com/yeasy/docker_practice",\
"github.com/AliyunContainerService/k8s-for-docker-desktop/raw",\
"dl-cdn.alpinelinux.org/alpine/edge/testing",\
"www.w3.org/1999/xhtml",\
"cr.console.aliyun.com",\
"cloud.tencent.com",\
"nodejs.org/dist/",\
"c.163.com/hub",\
"drone.yeasy.com",\
"docs.docker.com",\
"dockerhub.azk8s.cn",\
"reg-mirror.qiniu.com",\
"registry.docker-cn.com",\
"mirror.ccs.tencentyun.com",\
"vuepress.mirror.docker-practice.com",\
"mc.qcloudimg.com/static/img",\
"www.daocloud.io/mirror",\
"download.docker.com",\
"www.ubuntu.com",\
"archive.ubuntu.com",\
"security.ubuntu.com/ubuntu",\
"nginx.com",\
"img.shields.io/github/release/yeasy/docker_practice",\
"launchpad.net",\
"www.w3.org/1999",\
"chat.freenode.net",\
"en.wikipedia.org/wiki/UnionFS",\
"product.china-pub.com",\
"union-click.jd.com",\
"x.x.x.x/base",\
"x.x.x.x:9090",\
"yeasy.gitbooks.io",\
"download.fastgit.org",\
"www.aliyun.com" \
--allow-dupe \
--skip-save-results \
-t 10 \
`find . \( -path "./mesos" -o -path "./swarm_mode" \) -prune -o -name "*.md" -exec ls {} \;`
name: check-link
timeout-minutes: 25
================================================
FILE: .github/workflows/ci.yaml
================================================
name: CI
on:
push:
pull_request:
workflow_dispatch:
defaults:
run:
shell: bash --noprofile --norc -exo pipefail {0}
jobs:
build:
name: Build
runs-on: ubuntu-latest
steps:
- uses: actions/checkout@v6.0.2
- uses: actions/setup-node@v6
with:
node-version: 20
- name: Install docs dependencies
run: npm install
- name: Build Gitbook
run: npm run honkit:build
- name: Build Gitbook Docker Image
if: github.repository == 'docker-practice/docker_practice'
run: |
sudo chmod -R 777 _book
echo "FROM nginx:alpine" >> Dockerfile
echo "COPY _book /usr/share/nginx/html" >> Dockerfile
echo "COPY .docker/docker-entrypoint.sh /" >> Dockerfile
echo "ENTRYPOINT [\"/docker-entrypoint.sh\"]" >> Dockerfile
export VCS_REF=`git rev-parse --short HEAD`
docker build \
-t dockerpracticesig/docker_practice \
-t dockerpracticesig/docker_practice:gitbook \
--label org.opencontainers.image.revision=$VCS_REF \
--label org.opencontainers.image.source="https://github.com/yeasy/docker_practice" \
--label maintainer="https://github.com/docker-practice" \
.
docker run -d --rm -p 4000:80 dockerpracticesig/docker_practice
sleep 5
echo "::group::Test"
curl 127.0.0.1:4000
echo "::endgroup::"
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
docker push dockerpracticesig/docker_practice
docker push dockerpracticesig/docker_practice:gitbook
env:
DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}}
- name: Upload Gitbook dist
uses: docker://pcit/pages
if: github.repository == 'docker-practice/docker_practice'
env:
PCIT_EMAIL: khs1994@khs1994.com
PCIT_GIT_TOKEN: ${{ secrets.PCIT_GIT_TOKEN }}
PCIT_GIT_URL: github.com/docker-practice/zh-cn
PCIT_KEEP_HISTORY: "true"
PCIT_LOCAL_DIR: _book
PCIT_MESSAGE: Build from yeasy/docker_practice@${{github.sha}}
PCIT_TARGET_BRANCH: master
PCIT_USERNAME: khs1994
- name: vuepress
run: |
export NODE_OPTIONS=--openssl-legacy-provider
sudo rm -rf _book
git clone https://github.com/docker-practice/.vuepress .vuepress2
cp -r .vuepress2/. .vuepress/
rm -rf .vuepress2
find . \( -path "./mesos" -o -path "./swarm_mode" -o -path "./node_modules" -o -path "./.vuepress" -o -path "./_book" -o -path "./CHANGELOG.md" -o -path "./CONTRIBUTING.md" \) -prune -o -name "*.md" -exec sed -i 'N;2a\<AdSenseTitle/>\n' {} \;
npx vuepress --version
npm run vuepress:build
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
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
uses: docker://pcit/pages
if: github.repository == 'docker-practice/docker_practice'
env:
PCIT_EMAIL: khs1994@khs1994.com
PCIT_GIT_TOKEN: ${{ secrets.PCIT_GIT_TOKEN }}
PCIT_GIT_URL: github.com/docker-practice/vuepress
PCIT_KEEP_HISTORY: "true"
PCIT_LOCAL_DIR: .vuepress/dist
PCIT_MESSAGE: Build from yeasy/docker_practice@${{github.sha}}
PCIT_TARGET_BRANCH: master
PCIT_USERNAME: khs1994
# - name: Set coding.net CNAME
# run: |
# echo "vuepress.mirror.docker-practice.com" > .vuepress/dist/CNAME
# - name: Upload Vuepress dist to coding.net
# uses: docker://pcit/pages
# if: github.repository == 'docker-practice/docker_practice'
# env:
# PCIT_EMAIL: khs1994@khs1994.com
# PCIT_GIT_TOKEN: ${{ secrets.CODING_GIT_TOKEN }}
# PCIT_GIT_URL: e.coding.net/dpsigs/docker_practice
# PCIT_KEEP_HISTORY: "true"
# PCIT_LOCAL_DIR: .vuepress/dist
# PCIT_MESSAGE: Build from yeasy/docker_practice@${{github.sha}}
# PCIT_TARGET_BRANCH: master
# PCIT_USERNAME: ptt0xjqzbke3
- name: Build vuepress docker image
if: github.repository == 'docker-practice/docker_practice'
run: |
sudo rm -rf .vuepress/dist/.git
echo "FROM nginx:alpine" > Dockerfile
echo "COPY .vuepress/dist /usr/share/nginx/html" >> Dockerfile
echo "COPY .docker/docker-entrypoint.sh /" >> Dockerfile
echo "ENTRYPOINT [\"/docker-entrypoint.sh\"]" >> Dockerfile
echo "$DOCKER_PASSWORD" | docker login -u "$DOCKER_USERNAME" --password-stdin
VCS_REF=`git rev-parse --short HEAD`
docker build -t dockerpracticesig/docker_practice:vuepress \
--label org.opencontainers.image.revision=$VCS_REF \
--label org.opencontainers.image.source="https://github.com/yeasy/docker_practice" \
--label maintainer="https://github.com/docker-practice" \
.
docker push dockerpracticesig/docker_practice:vuepress
docker run -it --rm -d -p 4001:80 dockerpracticesig/docker_practice:vuepress
sleep 5
echo "::group::Test"
curl 127.0.0.1:4001
echo "::endgroup::"
env:
DOCKER_PASSWORD: ${{secrets.DOCKER_PASSWORD}}
DOCKER_USERNAME: ${{secrets.DOCKER_USERNAME}}
================================================
FILE: .github/workflows/dependabot-automerge.yml
================================================
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}}
================================================
FILE: .github/workflows/release-pdf.yml
================================================
name: Build PDF on Release
on:
release:
types: [published]
workflow_dispatch:
permissions:
contents: write
jobs:
build-pdf:
name: Generate PDF
runs-on: ubuntu-latest
steps:
- name: Checkout code
uses: actions/checkout@v4
- name: Install Chromium
run: |
sudo apt-get update
sudo apt-get install -y chromium-browser
- name: Install mdpress (latest)
run: |
LATEST_TAG=$(curl -fsSL https://api.github.com/repos/yeasy/mdpress/releases/latest | jq -r .tag_name)
VERSION="${LATEST_TAG#v}"
echo "Installing mdpress $VERSION"
curl -fsSL "https://github.com/yeasy/mdPress/releases/download/$LATEST_TAG/mdpress_${VERSION}_linux_amd64.tar.gz" -o mdpress.tar.gz
tar xzf mdpress.tar.gz
sudo mv mdpress /usr/local/bin/
mdpress --version
- name: Build PDF
run: mdpress build --format pdf --output docker_practice.pdf
- name: Upload PDF to Release
if: github.event_name == 'release'
uses: softprops/action-gh-release@v2
with:
files: docker_practice.pdf
- name: Upload PDF as artifact
if: github.event_name == 'workflow_dispatch'
uses: actions/upload-artifact@v4
with:
name: docker_practice-pdf
path: docker_practice.pdf
================================================
FILE: .gitignore
================================================
# Created by .gitignore support plugin (hsz.mobi)
*.~
*.tmp
.idea/
_book/
format_report.txt
*.swp
*.edx
.DS_Store
node_modules/
package-lock.json
docker-compose.override.yml
# Editor configs
.obsidian/
.vscode/
.agent/
__pycache__/
# Check scripts
check*.py
find*.py
fix*.py
format*.py
================================================
FILE: .vuepress/.gitignore
================================================
/*
!.gitignore
!config.js
================================================
FILE: .vuepress/config.js
================================================
const { config } = require('vuepress-theme-hope')
module.exports = config({
title: 'Docker 从入门到实践',
base: '/',
head: [['script', {}, `
var _hmt = _hmt || [];
(function() {
var hm = document.createElement("script");
hm.src = "//hm.baidu.com/hm.js?81a3490c9cd141dbcf6d00bc18b6edae";
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(hm, s);
})();
`],
[
'script', {}, `
(function(){
var bp = document.createElement('script');
var curProtocol = window.location.protocol.split(':')[0];
if (curProtocol === 'https') {
bp.src = 'https://zz.bdstatic.com/linksubmit/push.js';
}
else {
bp.src = 'http://push.zhanzhang.baidu.com/push.js';
}
var s = document.getElementsByTagName("script")[0];
s.parentNode.insertBefore(bp, s);
})();
`
]
],
plugins: {
// sitemap: {
// hostname: 'https://vuepress.mirror.docker-practice.com'
// },
// 'git-log': {
// additionalArgs: '--no-merge',
// onlyFirstAndLastCommit: true,
// },
},
locales: {
"/": {
lang: "zh-CN"
}
},
themeConfig: {
blog: false,
// comment: false,
comment: {
type: "disable", // 使用 Valine
appId: "...", // your appId
appKey: "...", // your appKey
},
pageInfo: [
// 'author',
'reading-time',
'word',
],
footer: {
content: "Made with <a target='_blank' href='https://github.com/vuepress-theme-hope/vuepress-theme-hope'>vuepress-theme-hope</a>",
display: true,
copyright: false,
},
searchPlaceholder: 'Search',
repo: 'yeasy/docker_practice',
repoLabel: 'GitHub',
repoDisplay: true,
hostname: 'https://vuepress.mirror.docker-practice.com',
// author: 'yeasy',
mdEnhance: {
lineNumbers: true,
},
git: {
contributor: false,
},
themeColor: {
blue: '#2196f3',
// red: '#f26d6d',
// green: '#3eaf7c',
// orange: '#fb9b5f'
},
locales: {
"/": {
lang: "zh-CN"
}
},
darkmode: 'auto-switch',
//
showAds: true,
docsRepo: 'yeasy/docker_practice',
docsDir: '/',
docsBranch: 'master',
editLinks: true,
nav: [
{
text: '微信交流群',
link: 'https://docker_practice.gitee.io/pic/dpsig-wechat.jpg',
},
{
text: '小程序',
link: 'https://docker_practice.gitee.io/pic/dp-wechat-miniprogram.jpg',
},
{
text: '安装 Docker',
link: '/03_install/',
},
{
text: 'Docker 入门',
link: '/'
},
{
text: 'Docker 实战',
link: '/15_cases/os/'
},
{
text: 'CI/CD',
link: '/15_cases/ci/'
},
{
text: 'Compose',
link: '/10_compose/',
},
{
text: 'Kubernetes',
link: '/12_orchestration/kubernetes/',
},
{
text: "云计算",
link: "/13_ecosystem/cloud/",
},
// {
// text: 'GitHub',
// link: 'https://github.com/yeasy/docker_practice'
// },
// {
// text: '捐赠',
// link: ''
// },
{
text: '云服务器99/元首年特惠',
link: 'https://cloud.tencent.com/act/cps/redirect?redirect=1062&cps_key=3a5255852d5db99dcd5da4c72f05df61&from=console'
},
// {
// text: '语言',
// items: [{
// text: 'English',
// link: ''
// }]
// }
],
sidebar: "auto",
legacySidebar: {
'/cloud/': [
'intro',
'tencentCloud',
'alicloud',
'aws',
'summary',
],
'/kubernetes/': [
'intro',
'concepts',
'design',
{
title: "部署 Kubernetes",
collapsable: true,
children: [
"setup/",
"setup/kubeadm",
"setup/docker-desktop",
"setup/systemd",
"setup/dashboard",
]
},
{
title: "Kubernetes 命令行 kubectl",
collapsable: true,
children: [
'kubectl/'
]
}
],
'/compose/': [
'introduction',
'v2',
'install',
'usage',
'commands',
'compose_file',
'django',
'rails',
'wordpress',
'lnmp',
],
'/install/': [
'ubuntu',
'debian',
'fedora',
'centos',
'raspberry-pi',
// 'offline',
'mac',
'windows',
'mirror',
'experimental',
],
'/cases/os/': [
{
title: "操作系统",
collapsable: false,
children: [
'busybox',
'alpine',
'debian',
'centos',
'summary',
],
},
{
title: "在 IDE 中使用 Docker",
collapsable: false,
children: [
'/ide/',
'/ide/vsCode',
],
},
],
'/cases/ci/': [
'actions/',
{
title: "Drone",
collapsable: true,
children: [
'drone/',
'drone/install'
]
},
],
'/': [
'/',
'/CHANGELOG',
'/CONTRIBUTING',
{
title: "Docker 简介",
collapsable: false,
children: [
'introduction/',
'introduction/what',
'introduction/why',
]
}, {
title: "基本概念",
collapsable: false,
children: [
'basic_concept/',
'basic_concept/image',
'basic_concept/container',
'basic_concept/repository'
]
},
{
title: "使用镜像",
collapsable: false,
children: [
'image/',
'image/pull',
'image/list',
'image/rm',
'image/commit',
'image/build',
'image/other.md',
'image/internal.md',
]
},
{
title: 'Dockerfile',
collapsable: true,
children: [
"image/dockerfile/",
'image/dockerfile/copy',
'image/dockerfile/add',
'image/dockerfile/cmd',
'image/dockerfile/entrypoint',
'image/dockerfile/env',
'image/dockerfile/arg',
'image/dockerfile/volume',
'image/dockerfile/expose',
'image/dockerfile/workdir',
'image/dockerfile/user',
'image/dockerfile/healthcheck',
'image/dockerfile/label',
'image/dockerfile/shell',
'image/dockerfile/onbuild',
'image/dockerfile/references',
'image/dockerfile/7.17_multistage_builds.md',
'image/dockerfile/7.18_multistage_builds_laravel.md',
'image/manifest',
]
}, {
title: "操作容器",
collapsable: false,
children: [
'container/',
'container/run',
'container/daemon',
'container/stop',
'container/attach_exec',
'container/import_export',
'container/rm',
],
},
{
title: "Docker 仓库",
collapsable: false,
children: [
'repository/',
'repository/dockerhub',
'repository/registry',
'repository/registry_auth',
'repository/nexus3_registry',
],
},
{
title: "数据管理",
collapsable: false,
children: [
'data_management/',
'data_management/volume',
'data_management/bind-mounts',
],
}, {
title: "使用网络",
collapsable: false,
children: [
'network/',
'network/port_mapping',
'network/linking',
'network/dns',
],
},
{
title: "高级网络配置",
collapsable: true,
children: [
'advanced_network/',
'advanced_network/quick_guide',
'advanced_network/access_control',
'advanced_network/port_mapping',
'advanced_network/bridge',
'advanced_network/example',
'advanced_network/config_file',
'advanced_network/ptp',
],
},
{
title: "Swarm mode",
collapsable: true,
children: [
'swarm_mode/',
'swarm_mode/overview',
'swarm_mode/create',
'swarm_mode/deploy',
'swarm_mode/stack',
'swarm_mode/secret',
'swarm_mode/config',
'swarm_mode/rolling_update',
],
},
{
title: "安全",
collapsable: true,
children: [
'security/',
'security/kernel_ns',
'security/control_group',
'security/daemon_sec',
'security/kernel_capability',
'security/other_feature',
'security/summary',
],
},
{
title: "底层实现",
collapsable: true,
children: [
'underly/',
'underly/arch',
'underly/namespace',
'underly/cgroups',
'underly/ufs',
'underly/container_format',
'underly/network',
],
},
{
title: "Docker Buildx",
collapsable: false,
children: [
"buildx/",
"buildx/buildkit",
"buildx/buildx",
"buildx/multi-arch-images",
],
},
{
title: "Etcd",
collapsable: true,
children: [
'etcd/',
'etcd/intro',
'etcd/install',
'etcd/cluster',
'etcd/etcdctl',
],
},
{
title: "Fedora CoreOS",
collapsable: true,
children: [
'coreos/',
'coreos/intro',
'coreos/install',
],
},
'podman/',
'appendix/faq/',
{
title: "热门镜像介绍",
collapsable: true,
children: [
'appendix/repo/',
'appendix/repo/ubuntu',
'appendix/repo/centos',
'appendix/repo/nginx',
'appendix/repo/php',
'appendix/repo/nodejs',
'appendix/repo/mysql',
'appendix/repo/wordpress',
'appendix/repo/mongodb',
'appendix/repo/redis',
'appendix/repo/minio',
],
},
{
title: "Docker 命令",
collapsable: true,
children: [
'appendix/command/',
'appendix/command/docker',
'appendix/command/dockerd',
]
},
'appendix/best_practices',
'appendix/debug',
'appendix/resources',
],
},
}
});
================================================
FILE: .zhlintignore
================================================
node_modules/
.vuepress/
.git/
================================================
FILE: 01_introduction/1.1_quickstart.md
================================================
## 1.1 快速上手
本节将通过一个简单的 Web 应用例子,带你快速体验 Docker 的核心流程:构建镜像、运行容器。
### 1.1.1 准备代码
创建一个名为 `hello-docker` 的文件夹,并在其中创建一个 `index.html` 文件:
```html
<h1>Hello, Docker!</h1>
```
### 1.1.2 编写 Dockerfile
在同级目录下创建一个名为 `Dockerfile` (无后缀) 的文件:
```dockerfile
FROM nginx:alpine
COPY index.html /usr/share/nginx/html/index.html
```
### 1.1.3 构建镜像
打开终端,进入该目录,执行构建命令:
```bash
$ docker build -t my-hello-world .
```
* `docker build`:构建命令
* `-t my-hello-world`:给镜像起个名字 (标签)
* `.`:指定上下文路径为当前目录
### 1.1.4 运行容器
使用刚才构建的镜像启动一个容器:
```bash
$ docker run -d -p 8080:80 my-hello-world
```
* `docker run`:运行命令
* `-d`:后台运行
* `-p 8080:80`:将宿主机的 8080 端口映射到容器的 80 端口
### 1.1.5 访问测试
打开浏览器访问 [http://localhost:8080](http://localhost:8080),你应该能看到 “Hello, Docker!”。
### 1.1.6 清理
停止并删除容器:
```bash
## 查看正在运行的容器 ID
$ docker ps
## 停止容器
$ docker stop <CONTAINER_ID>
## 删除容器
$ docker rm <CONTAINER_ID>
```
恭喜!你已经完成了第一次 Docker 实战。接下来请阅读 [Docker 核心概念](../02_basic_concept/README.md)做深入了解。
================================================
FILE: 01_introduction/1.2_what.md
================================================
## 1.2 什么是 Docker
Docker 是彻底改变了软件开发和交付方式的革命性技术。本节将从核心概念、与传统虚拟机的对比、技术基础以及历史生态等多个维度,带你深入理解什么是 Docker。
### 1.2.1 一句话理解 Docker
> **Docker 是一种轻量级的虚拟化技术,它让应用程序及其依赖环境可以被打包成一个标准化的单元,在任何地方都能一致地运行。** 如果用一个生活中的类比:**Docker 之于软件,就像集装箱之于货物**。
在集装箱发明之前,货物的运输是一件麻烦的事情——不同的货物需要不同的包装、不同的装卸方式,换一种运输工具就要重新装卸。集装箱的出现改变了这一切:无论里面装的是什么,集装箱的外形是标准的,可以用同样的方式装卸、堆放和运输。
Docker 做的事情类似:无论你的应用是用 Python、Java、Node.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 时会问:**“这不就是虚拟机吗?”** 答案是:**不是,而且差别很大。**
#### 传统虚拟机
传统虚拟机技术是虚拟出一套完整的硬件,在其上运行一个完整的操作系统,再在该系统上运行应用:

#### Docker 容器
而 Docker 容器内的应用直接运行于宿主的内核,容器内没有自己的内核,也没有进行硬件虚拟:

#### 关键区别
| 特性 | 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** (2014,v0.7):Docker 自研的容器运行时
- **runC** (2015,v1.11):捐献给 OCI 的标准容器运行时
- **containerd**:高级容器运行时,管理容器生命周期

> `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 公司改名为 Docker,Inc。
- **2015 年**:成立[开放容器联盟 (OCI)](https://opencontainers.org/),推动容器标准化
- **至今**:[GitHub 项目](https://github.com/moby/moby)超过 7 万星标
Docker 的成功推动了整个容器生态的发展,催生了 Kubernetes、Podman 等众多相关项目。笔者认为,Docker 最大的贡献不仅是技术本身,更是它 **让容器技术从系统管理员的工具变成了每个开发者都能使用的标准工具**。
================================================
FILE: 01_introduction/1.3_why.md
================================================
## 1.3 为什么要用 Docker
在回答 “为什么用 Docker” 之前,笔者想先问一个问题:**你有没有经历过这些场景?**
### 1.3.1 没有 Docker 的世界
在 Docker 出现之前,软件开发和运维面临着诸多棘手的问题。我们先来看看以下三个典型的痛点场景。
#### 场景一:“在我电脑上明明能跑”
```text
周五下午 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 可以在几乎任何平台上运行:
- ✅ 本地开发机 (macOS、Windows、Linux)
- ✅ 公有云 (AWS、Azure、GCP、阿里云、腾讯云)
- ✅ 私有云和自建数据中心
- ✅ 边缘设备和 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、开发环境;虚拟机适合多租户、高安全需求场景。
================================================
FILE: 01_introduction/README.md
================================================
# 第一章 Docker 简介
本章将带领你进入 **Docker** 的世界。
> **版本提示**:本书内容及示例基于 **Docker Engine v29.x** 及以上版本。值得注意的是,自 Docker Engine v29 起,官方在全新安装场景下 **默认启用了 `containerd image store` 作为镜像存储后端**(取代了传统的经典存储引擎如 overlay2 graph driver)。这项底层革新极大增强了 Docker 对多架构镜像(Multi-platform)、以及软件供应链安全元数据(Attestations, SBOM, Provenance)的本地支持原生性。
## 本章内容
* [快速上手](1.1_quickstart.md)
* 通过一个简单的 Web 应用例子,带你快速体验 Docker 的核心流程:构建镜像、运行容器。
* [什么是 Docker](1.2_what.md)
* 介绍 Docker 的起源、发展历程以及其背后的核心技术 (Cgroups,Namespaces,UnionFS,以及 `containerd` 引擎的演进)。
* 了解 Docker 是如何改变软件交付方式的。
* [为什么要用 Docker](1.3_why.md)
* 对比传统虚拟机技术,阐述 Docker 在启动速度、资源利用率、交付效率等方面的巨大优势。
* 探讨 Docker 在 DevOps、微服务架构中的关键作用。
## 学习目标
通过本章的学习,你将能够:
1. 理解 Docker 的核心概念与架构。
2. 明白 Docker 解决了现代软件开发与运维中的哪些痛点。
3. 建立起对容器技术的初步认知,为后续的实战操作打下基础。
好吧,让我们带着问题开始这神奇之旅。
================================================
FILE: 01_introduction/summary.md
================================================
## 本章小结
- Docker 是一种轻量级虚拟化技术,核心价值是 **环境一致性**
- 与虚拟机相比,Docker 更轻量、更快速、资源利用率更高
- Docker 基于 Linux 内核的 Namespace、Cgroups 和 Union FS 技术
- Docker 推动了容器技术的标准化 (OCI) 和生态发展
Docker 的核心价值可以用一句话概括:**让应用的开发、测试、部署保持一致,同时极大提高资源利用效率。** 笔者认为,对于现代软件开发者来说,Docker 已经不是 “要不要学” 的问题,而是 **必备技能**。无论你是前端、后端、运维还是全栈开发者,掌握 Docker 都能让你的工作更高效。
---
> 📝 **发现错误或有改进建议?** 欢迎提交 [Issue](https://github.com/yeasy/docker_practice/issues) 或 [PR](https://github.com/yeasy/docker_practice/pulls)。
================================================
FILE: 02_basic_concept/2.1_image.md
================================================
## 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 解释器 |
| **库文件** | libc、OpenSSL、各种依赖库 |
| **配置文件** | nginx.conf、my.cnf 等 |
| **环境变量** | PATH、LANG 等预设值 |
| **元数据** | 启动命令、暴露端口、数据卷定义 |
**关键特性**:
- ✅ 镜像是 **只读** 的
- ✅ 镜像 **不包含** 动态数据
- ✅ 镜像构建后 **内容不会改变**
### 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` |
================================================
FILE: 02_basic_concept/2.2_container.md
================================================
## 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)。
================================================
FILE: 02_basic_concept/2.3_repository.md
================================================
## 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:Registry、Repository 与 Tag 的层级关系
相关基本概念具体如下:
| 概念 | 说明 | 示例 |
|------|------|------|
| **Registry** | 存储镜像的服务 | Docker Hub、ghcr.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)** | 支持多种制品类型 (Docker、Maven、npm 等)|
| **云厂商服务** | 阿里云 ACR、腾讯云 TCR、AWS 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
```
================================================
FILE: 02_basic_concept/README.md
================================================
# 第二章 基本概念
**Docker** 包括三个基本概念:
* **镜像** (`Image`):Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数 (如匿名卷、环境变量、用户等)。镜像不包含任何动态数据,其内容在构建之后也不会被改变。
* **容器** (`Container`):镜像 (`Image`) 和容器 (`Container`) 的关系,就像是面向对象程序设计中的 `类` 和 `实例` 一样,镜像是静态的定义,容器是镜像运行时的实体。容器可以被创建、启动、停止、删除、暂停等。
* **仓库** (`Repository`):镜像构建完成后,可以很容易的在当前宿主机上运行,但是,如果需要在其它服务器上使用这个镜像,我们就需要一个集中的存储、分发镜像的服务,Docker Registry 就是这样的服务。
理解了这三个概念,就理解了 **Docker** 的整个生命周期。
## 本章内容
* [Docker 镜像](2.1_image.md)
* [Docker 容器](2.2_container.md)
* [Docker 仓库](2.3_repository.md)
================================================
FILE: 02_basic_concept/summary.md
================================================
## 本章小结
本章介绍了 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)。
================================================
FILE: 03_install/3.10_experimental.md
================================================
## 3.10 开启实验特性
一些 docker 命令或功能仅当 **实验特性** 开启时才能使用,请按照以下方法进行设置。
### 3.10.1 Docker CLI 的实验特性
CLI 的实验特性通常包含仍在开发中的新功能。幸运的是,在较新版本中这些特性已经更加易用。
从 `v20.10` 版本开始,Docker CLI 所有实验特性的命令均默认开启,无需再进行配置或设置系统环境变量。
### 3.10.2 开启 dockerd 的实验特性
编辑 `/etc/docker/daemon.json`,新增如下条目
```json
{
"experimental": true
}
```
================================================
FILE: 03_install/3.1_ubuntu.md
================================================
## 3.1 Ubuntu
Ubuntu 是 Docker 最常用的运行环境之一。本节将介绍如何在 Ubuntu 系统上安装 Docker,并配置国内镜像加速。
> 警告:切勿在没有配置 Docker APT 源的情况下直接使用 apt 命令安装 Docker。
### 3.1.1 准备工作
在开始安装之前,我们需要确认系统版本是否满足要求,并清理可能存在的旧版本。
#### 系统要求
Docker 支持诸多版本的 [Ubuntu](https://ubuntu.com/server) 操作系统。但是较旧的版本上将不会有 Docker 新版本的持续更新,以截至 2026 年初的几个 Ubuntu LTS (Long Term Support,长期支持) 版本为例:
* Ubuntu Noble 24.04 (LTS),Docker v29.x
* Ubuntu Jammy 22.04 (LTS), Docker v29.x
> **注意**:Ubuntu 20.04 LTS 已于 2025 年结束标准支持,不再推荐用于新部署。
在 Ubuntu LTS 版本上,目前 Docker 支持 amd64、arm64、armhf、ppc64el、s390x 等 5 个平台;而非 LTS 版本支持的平台通常较少。同时,LTS 版本会获得 5 年的升级维护支持,这样的系统会获得更长期的安全保障,因此在生产环境中推荐使用 LTS 版本。
#### 卸载旧版本
旧版本的 Docker 称为 `docker` 或者 `docker-engine`,使用以下命令卸载旧版本:
```bash
$ for pkg in docker \
docker-engine \
docker.io \
docker-doc \
podman-docker \
containerd \
runc;
do
sudo apt remove $pkg;
done
```
### 3.1.2 使用 APT 安装
由于 `apt` 源使用 HTTPS 以确保软件下载过程中不被篡改。因此,我们首先需要添加使用 HTTPS 传输的软件包以及 CA 证书。
```bash
$ sudo apt update
$ sudo apt install \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release
```
鉴于国内网络问题,强烈建议使用国内源,官方源请在注释中查看。
为了确认所下载软件包的合法性,需要添加软件源的 `GPG` 密钥。
```bash
$ 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
```
然后,我们需要向 `sources.list` 中添加 Docker 软件源
```bash
$ echo \
"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.aliyun.com/docker-ce/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 \
# $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```
> 以上命令会添加稳定版本的 Docker APT 镜像源,如果需要测试版本的 Docker 请将 stable 改为 test。
#### 安装 Docker
更新 apt 软件包缓存,并安装 `docker-ce`:
```bash
$ sudo apt update
$ sudo apt install docker-ce docker-ce-cli containerd.io
```
### 3.1.3 使用脚本自动安装
在测试或开发环境中 Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,Ubuntu 系统上可以使用这套脚本安装,另外可以通过 `--mirror` 选项使用国内源进行安装:
> 若你想安装测试版的 Docker,请从 test.docker.com 获取脚本
```bash
# $ 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
```
执行这个命令后,脚本就会自动的将一切准备工作做好,并且把 Docker 的稳定 (stable) 版本安装在系统中。
### 3.1.4 启动 Docker
```bash
$ sudo systemctl enable docker
$ sudo systemctl start 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` 用户组等同于 `root` 权限**
>
> 将用户加入 `docker` 组免去了每次执行 `docker` 命令时输入 `sudo` 的繁琐,但这也意味着该用户可以轻易获取主机的最高 root 权限(例如通过挂载根目录运行容器)。
> 如果你在一个多用户共享的生产系统上配置,切勿随意将普通用户加入此组。此时,更安全的替代方案是使用官方提供的 **[Rootless 模式 (Rootless mode)](https://docs.docker.com/engine/security/rootless/)**,它允许在没有任何 root 权限的情况下运行 Docker 守护进程和容器。
建立 `docker` 组:
```bash
$ sudo groupadd docker
```
将当前用户加入 `docker` 组:
```bash
$ sudo usermod -aG docker $USER
```
退出当前终端并重新登录,进行如下测试。
### 3.1.6 测试 Docker 是否安装正确
```bash
$ docker run --rm hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete
Digest: sha256:308866a43596e83578c7dfa15e27a73011bdd402185a84c5cd7f32a88b501a24
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
```
若能正常输出以上信息,则说明安装成功。
### 3.1.7 镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢,可以配置 Docker [国内镜像加速](3.9_mirror.md)。
### 3.1.8 参考文档
* [Docker 官方 Ubuntu 安装文档](https://docs.docker.com/engine/install/ubuntu/)
================================================
FILE: 03_install/3.2_debian.md
================================================
## 3.2 Debian
Debian 以其稳定性著称,是 Docker 的理想宿主系统。本节将指导你在 Debian 上完成 Docker 的安装。
> 警告:切勿在没有配置 Docker APT 源的情况下直接使用 apt 命令安装 Docker。
### 3.2.1 准备工作
安装前请仔细检查 Debian 版本支持情况,并卸载旧版本以避免冲突。
#### 系统要求
Docker 支持以下版本的 [Debian](https://www.debian.org/intro/about) 操作系统:
* Debian Trixie 13 (stable)
* Debian Bookworm 12 (oldstable)
* Debian Bullseye 11 (LTS)
#### 卸载旧版本
旧版本的 Docker 称为 `docker` 或者 `docker-engine`,使用以下命令卸载旧版本:
```bash
$ sudo apt-get remove docker \
docker-engine \
docker.io
```
### 3.2.2 使用 APT 安装
由于 apt 源使用 HTTPS 以确保软件下载过程中不被篡改。因此,我们首先需要添加使用 HTTPS 传输的软件包以及 CA 证书。
```bash
$ sudo apt-get update
$ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release
```
鉴于国内网络问题,强烈建议使用国内源,官方源请在注释中查看。
为了确认所下载软件包的合法性,需要添加软件源的 GPG 密钥。
```bash
$ 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
```
然后,我们需要向 `sources.list` 中添加 Docker 软件源:
> 在一些基于 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 \
"deb [arch=amd64 signed-by=/usr/share/keyrings/docker-archive-keyring.gpg] https://mirrors.aliyun.com/docker-ce/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 \
# $(lsb_release -cs) 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-ce`。
```bash
$ 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 获取脚本
```bash
# $ 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
```
执行这个命令后,脚本就会自动的将一切准备工作做好,并且把 Docker 的稳定 (stable) 版本安装在系统中。
### 3.2.4 启动 Docker
```bash
$ sudo systemctl enable docker
$ sudo systemctl start 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` 用户组等同于 `root` 权限**
>
> 将用户加入 `docker` 组免去了每次执行 `docker` 命令时输入 `sudo` 的繁琐,但这也意味着该用户可以轻易获取主机的最高 root 权限(例如通过挂载根目录运行容器)。
> 如果你在一个多用户共享的生产系统上配置,切勿随意将普通用户加入此组。此时,更安全的替代方案是使用官方提供的 **[Rootless 模式 (Rootless mode)](https://docs.docker.com/engine/security/rootless/)**,它允许在没有任何 root 权限的情况下运行 Docker 守护进程和容器。
建立 `docker` 组:
```bash
$ sudo groupadd docker
```
将当前用户加入 `docker` 组:
```bash
$ sudo usermod -aG docker $USER
```
退出当前终端并重新登录,进行如下测试。
### 3.2.6 测试 Docker 是否安装正确
```bash
$ docker run --rm hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete
Digest: sha256:308866a43596e83578c7dfa15e27a73011bdd402185a84c5cd7f32a88b501a24
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
```
若能正常输出以上信息,则说明安装成功。
### 3.2.7 镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢,可以配置 Docker [国内镜像加速](3.9_mirror.md)。
### 3.2.8 参考文档
* [Docker 官方 Debian 安装文档](https://docs.docker.com/engine/install/debian/)
================================================
FILE: 03_install/3.3_fedora.md
================================================
## 3.3 Fedora
Fedora 作为技术前沿的 Linux 发行版,对 Docker 有着良好的支持。本节介绍在 Fedora 上的安装步骤。
> 警告:切勿在没有配置 Docker dnf 源的情况下直接使用 dnf 命令安装 Docker。
### 3.3.1 准备工作
确保你的 Fedora 版本在支持列表中,并清理旧版本。
#### 系统要求
Docker 支持以下版本的 [Fedora](https://getfedora.org/) 操作系统:
* 41
* 42
* 43
#### 卸载旧版本
旧版本的 Docker 称为 `docker` 或者 `docker-engine`,使用以下命令卸载旧版本:
```bash
$ sudo dnf remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine
```
### 3.3.2 使用 dnf 安装
使用 dnf 包管理器安装是推荐的方式,便于后续的更行和管理。
执行以下命令安装依赖包:
```bash
$ sudo dnf -y install dnf-plugins-core
```
鉴于国内网络问题,强烈建议使用国内源,官方源请在注释中查看。
执行下面的命令添加 `dnf` 软件源:
```bash
$ sudo dnf config-manager \
--add-repo \
https://mirrors.aliyun.com/docker-ce/linux/fedora/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/fedora/docker-ce.repo
```
如果需要测试版本的 Docker 请使用以下命令:
```bash
$ sudo dnf config-manager --set-enabled docker-ce-test
```
你也可以禁用测试版本的 Docker
```bash
$ sudo dnf config-manager --set-disabled docker-ce-test
```
#### 安装 Docker
更新 `dnf` 软件源缓存,并安装 `docker-ce`。
```bash
$ sudo dnf update
$ sudo dnf install docker-ce docker-ce-cli containerd.io
```
你也可以使用以下命令安装指定版本的 Docker
```bash
$ dnf list docker-ce --showduplicates | sort -r
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 官方为了简化安装流程,提供了一套便捷的安装脚本,Fedora 系统上可以使用这套脚本安装,另外可以通过 `--mirror` 选项使用国内源进行安装:
> 若你想安装测试版的 Docker,请从 test.docker.com 获取脚本
```bash
# $ 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
```
执行这个命令后,脚本就会自动的将一切准备工作做好,并且把 Docker 最新稳定 (stable) 版本安装在系统中。
### 3.3.4 启动 Docker
```bash
$ sudo systemctl enable docker
$ sudo systemctl start 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` 用户组等同于 `root` 权限**
>
> 将用户加入 `docker` 组免去了每次执行 `docker` 命令时输入 `sudo` 的繁琐,但这也意味着该用户可以轻易获取主机的最高 root 权限(例如通过挂载根目录运行容器)。
> 如果你在一个多用户共享的生产系统上配置,切勿随意将普通用户加入此组。此时,更安全的替代方案是使用官方提供的 **[Rootless 模式 (Rootless mode)](https://docs.docker.com/engine/security/rootless/)**,它允许在没有任何 root 权限的情况下运行 Docker 守护进程和容器。
建立 `docker` 组:
```bash
$ sudo groupadd docker
```
将当前用户加入 `docker` 组:
```bash
$ sudo usermod -aG docker $USER
```
退出当前终端并重新登录,进行如下测试。
### 3.3.6 测试 Docker 是否安装正确
```bash
$ docker run --rm hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete
Digest: sha256:308866a43596e83578c7dfa15e27a73011bdd402185a84c5cd7f32a88b501a24
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
```
若能正常输出以上信息,则说明安装成功。
### 3.3.7 镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢,可以配置 Docker [国内镜像加速](3.9_mirror.md)。
### 3.3.8 参考文档
* [Docker 官方 Fedora 安装文档](https://docs.docker.com/engine/install/fedora/)。
================================================
FILE: 03_install/3.4_centos.md
================================================
## 3.4 CentOS
CentOS (及其替代品 Rocky Linux、AlmaLinux) 是企业级服务器常用的操作系统。本节介绍在这些系统上安装 Docker 的步骤。
> 警告:切勿在没有配置 Docker YUM 源的情况下直接使用 yum 命令安装 Docker。
### 3.4.1 准备工作
安装前请确认系统版本和内核版本满足 Docker 的运行要求。
#### 系统要求
> ⚠️ **重要提示**:CentOS 8 已于 2021 年 12 月 31 日停止维护,CentOS 7 已于 2024 年 6 月 30 日结束支持。建议新项目使用 **Rocky Linux** 或 **AlmaLinux** 作为替代。
Docker 支持 64 位版本 CentOS Stream 9、Rocky Linux 8/9、AlmaLinux 8/9,并且要求内核版本不低于 3.10。
对于 Rocky Linux、AlmaLinux 或 CentOS Stream,推荐使用 `dnf` 包管理器。
#### 卸载旧版本
旧版本的 Docker 称为 `docker` 或者 `docker-engine`,使用以下命令卸载旧版本:
```bash
$ sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-selinux \
docker-engine-selinux \
docker-engine \
docker-ce-cli \
containerd.io
```
### 3.4.2 使用 yum 安装
使用 yum/dnf 安装是管理 Docker 生命周期的标准方式。
执行以下命令安装依赖包:
```bash
$ sudo dnf install -y dnf-utils
```
鉴于国内网络问题,强烈建议使用国内源,官方源请在注释中查看。
执行下面的命令添加 `yum` 软件源:
```bash
$ 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/yum.repos.d/docker-ce.repo
# 官方源
# $ sudo dnf config-manager \
# --add-repo \
# https://download.docker.com/linux/centos/docker-ce.repo
```
如果需要测试版本的 Docker 请执行以下命令:
```bash
$ sudo dnf config-manager --set-enabled docker-ce-test
```
#### 安装 Docker
更新 `dnf` 软件源缓存,并安装 `docker-ce`。
```bash
$ sudo dnf install docker-ce docker-ce-cli containerd.io
```
### 3.4.3 CentOS8 额外设置
CentOS 8/Stream 默认使用 `nftables`。Docker 在新版本中已提供 `nftables` 实验支持,但在一些环境下仍可能遇到兼容性问题。若你遇到容器网络异常,可以先切换回 `iptables` 后端:
更改 `/etc/firewalld/firewalld.conf`
```bash
## FirewallBackend=nftables
FirewallBackend=iptables
```
或者执行如下命令:
```bash
$ firewall-cmd --permanent --zone=trusted --add-interface=docker0
$ firewall-cmd --reload
```
### 3.4.4 使用脚本自动安装
在测试或开发环境中 Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,CentOS 系统上可以使用这套脚本安装,另外可以通过 `--mirror` 选项使用国内源进行安装:
> 若你想安装测试版的 Docker,请从 test.docker.com 获取脚本
```bash
# $ 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
```
执行这个命令后,脚本就会自动的将一切准备工作做好,并且把 Docker 的稳定 (stable) 版本安装在系统中。
### 3.4.5 启动 Docker
```bash
$ sudo systemctl enable docker
$ sudo systemctl start 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` 用户组等同于 `root` 权限**
>
> 将用户加入 `docker` 组免去了每次执行 `docker` 命令时输入 `sudo` 的繁琐,但这也意味着该用户可以轻易获取主机的最高 root 权限(例如通过挂载根目录运行容器)。
> 如果你在一个多用户共享的生产系统上配置,切勿随意将普通用户加入此组。此时,更安全的替代方案是使用官方提供的 **[Rootless 模式 (Rootless mode)](https://docs.docker.com/engine/security/rootless/)**,它允许在没有任何 root 权限的情况下运行 Docker 守护进程和容器。
建立 `docker` 组:
```bash
$ sudo groupadd docker
```
将当前用户加入 `docker` 组:
```bash
$ sudo usermod -aG docker $USER
```
退出当前终端并重新登录,进行如下测试。
### 3.4.7 测试 Docker 是否安装正确
```bash
$ docker run --rm hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
b8dfde127a29: Pull complete
Digest: sha256:308866a43596e83578c7dfa15e27a73011bdd402185a84c5cd7f32a88b501a24
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
```
若能正常输出以上信息,则说明安装成功。
### 3.4.8 镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢,可以配置 Docker [国内镜像加速](3.9_mirror.md)。
### 3.4.9 添加内核参数
如果在 CentOS 使用 Docker 看到下面的这些警告信息:
```bash
WARNING: bridge-nf-call-iptables is disabled
WARNING: bridge-nf-call-ip6tables is disabled
```
请添加内核配置参数以启用这些功能。
```bash
$ sudo tee -a /etc/sysctl.conf <<-EOF
net.bridge.bridge-nf-call-ip6tables = 1
net.bridge.bridge-nf-call-iptables = 1
EOF
```
然后重新加载 `sysctl.conf` 即可
```bash
$ sudo sysctl -p
```
### 3.4.10 参考文档
* [Docker 官方 CentOS 安装文档](https://docs.docker.com/engine/install/centos/)。
* https://firewalld.org/2018/07/nftables-backend
* https://github.com/moby/libnetwork/issues/2496
================================================
FILE: 03_install/3.5_raspberry-pi.md
================================================
## 3.5 Raspberry Pi
树莓派等 ARM 架构设备在物联网和边缘计算领域应用广泛。本节介绍如何在树莓派上安装 Docker。
> 警告:切勿在没有配置 Docker APT 源的情况下直接使用 apt 命令安装 Docker。
### 3.5.1 系统要求
Docker 对 ARM 架构有着良好的支持。
Docker 不仅支持 `x86_64` 架构的计算机,同时也支持 `ARM` 架构的计算机,本小节内容以树莓派单片电脑为例讲解 `ARM` 架构安装 Docker。
Docker 支持以下版本的 [Raspberry Pi OS](https://www.raspberrypi.org/software/operating-systems/) 操作系统:
* Raspberry Pi OS Trixie
* Raspberry Pi OS Bookworm
* Raspberry Pi OS Bullseye
*注:*`Raspberry Pi OS` 由树莓派的开发与维护机构[树莓派基金会](https://www.raspberrypi.org/)官方支持,并推荐用作树莓派的首选系统,其基于 `Debian`。
### 3.5.2 使用 APT 安装
推荐使用 APT 包管理器进行安装,以确保版本的稳定性和安全性。
由于 apt 源使用 HTTPS 以确保软件下载过程中不被篡改。因此,我们首先需要添加使用 HTTPS 传输的软件包以及 CA 证书。
```bash
$ sudo apt-get update
$ sudo apt-get install \
apt-transport-https \
ca-certificates \
curl \
gnupg \
lsb-release
```
鉴于国内网络问题,强烈建议使用国内源,官方源请在注释中查看。
为了确认所下载软件包的合法性,需要添加软件源的 GPG 密钥。
```bash
$ 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
# 官方源
# $ 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
$ 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
# 官方源
# $ echo \
# "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/raspbian \
# $(lsb_release -cs) stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
```
> 以上命令会添加稳定版本的 Docker APT 源,如果需要测试版本的 Docker 请将 stable 改为 test。
#### 报错解决办法
在 `Raspberry Pi OS Bullseye/Bookworm` 中,如果你使用 `add-apt-repository` 添加源,可能会出现如下报错 (推荐改用上面的 `tee` 方式来写入 `/etc/apt/sources.list.d/docker.list`,可避免此问题):
```bash
Traceback (most recent call last):
File "/usr/bin/add-apt-repository", line 95, in <module>
sp = SoftwareProperties(options=options)
File "/usr/lib/python3/dist-packages/softwareproperties/SoftwareProperties.py", line 109, in __init__
self.reload_sourceslist()
File "/usr/lib/python3/dist-packages/softwareproperties/SoftwareProperties.py", line 599, in reload_sourceslist
self.distro.get_sources(self.sourceslist)
File "/usr/lib/python3/dist-packages/aptsources/distro.py", line 91, in get_sources
raise NoDistroTemplateException(
aptsources.distro.NoDistroTemplateException: Error: could not find a distribution template for Raspbian/bullseye
```
通过以下命令手动添加镜像源到 `/etc/apt/sources.list` 文件中即可解决:
```bash
$ 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
## 官方源
# $ echo "deb [arch=$(dpkg --print-architecture) signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/raspbian $(lsb_release -cs) stable" | sudo tee -a /etc/apt/sources.list
```
#### 安装 Docker
更新 apt 软件包缓存,并安装 `docker-ce`。
```bash
$ sudo apt-get update
$ sudo apt-get install docker-ce
```
### 3.5.3 使用脚本自动安装
在测试或开发环境中 Docker 官方为了简化安装流程,提供了一套便捷的安装脚本,Raspberry Pi OS 系统上可以使用这套脚本安装,另外可以通过 `--mirror` 选项使用国内源进行安装:
> 若你想安装测试版的 Docker,请从 test.docker.com 获取脚本
```bash
# $ 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
```
执行这个命令后,脚本就会自动的将一切准备工作做好,并且把 Docker 的稳定 (stable) 版本安装在系统中。
### 3.5.4 启动 Docker
```bash
$ sudo systemctl enable docker
$ sudo systemctl start 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` 用户组等同于 `root` 权限**
>
> 将用户加入 `docker` 组免去了每次执行 `docker` 命令时输入 `sudo` 的繁琐,但这也意味着该用户可以轻易获取主机的最高 root 权限(例如通过挂载根目录运行容器)。
> 如果你在一个多用户共享的生产系统上配置,切勿随意将普通用户加入此组。此时,更安全的替代方案是使用官方提供的 **[Rootless 模式 (Rootless mode)](https://docs.docker.com/engine/security/rootless/)**,它允许在没有任何 root 权限的情况下运行 Docker 守护进程和容器。
建立 `docker` 组:
```bash
$ sudo groupadd docker
```
将当前用户加入 `docker` 组:
```bash
$ sudo usermod -aG docker $USER
```
退出当前终端并重新登录,进行如下测试。
### 3.5.6 测试 Docker 是否安装正确
```bash
$ docker run --rm hello-world
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
4ee5c797bcd7: Pull complete
Digest: sha256:308866a43596e83578c7dfa15e27a73011bdd402185a84c5cd7f32a88b501a24
Status: Downloaded newer image for hello-world:latest
Hello from Docker!
This message shows that your installation appears to be working correctly.
To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(arm32v7)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.
To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash
Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/
For more examples and ideas, visit:
https://docs.docker.com/get-started/
```
若能正常输出以上信息,则说明安装成功。
*注意:*ARM 平台不能使用 `x86` 镜像,查看 Raspberry Pi OS 可使用镜像请访问 [arm32v7](https://hub.docker.com/u/arm32v7/) 或者 [arm64v8](https://hub.docker.com/u/arm64v8/)。
### 3.5.7 镜像加速
如果在使用过程中发现拉取 Docker 镜像十分缓慢,可以配置 Docker [国内镜像加速](3.9_mirror.md)。
================================================
FILE: 03_install/3.6_offline.md
================================================
## 3.6 Linux 离线安装
生产环境中一般都是没有公网资源的,本文介绍如何在生产服务器上离线部署 `Docker`
括号内的字母表示该操作需要在哪些服务器上执行

### 3.6.1 CentOS/Rocky/AlmaLinux 离线安装 Docker
在无法连接外网的安全环境中,离线安装是唯一的选择。本节介绍如何在 RHEL 系发行版中进行离线安装。
> 注意:以下命令以 CentOS 7 为例。对于 CentOS Stream 9、Rocky Linux 9 或 AlmaLinux 9,请将 `yum` 替换为 `dnf`,并将软件包后缀 `el7` 替换为 `el9`。
#### YUM 本地文件安装:推荐
推荐这种方式,是因为在生产环境中一般会选定某个指定的文档软件版本使用。
##### 查询可用的软件版本
```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
yum update
```
```bash
sudo yum list docker-ce --showduplicates|sort -r
Loading mirror speeds from cached hostfile
Loaded plugins: fastestmirror
docker-ce.x86_64 24.0.4-1.el7 docker-ce-stable
docker-ce.x86_64 3:19.03.7-3.el7 docker-ce-stable
docker-ce.x86_64 3:19.03.6-3.el7 docker-ce-stable
docker-ce.x86_64 3:19.03.5-3.el7 docker-ce-stable
docker-ce.x86_64 3:19.03.4-3.el7 docker-ce-stable
docker-ce.x86_64 3:19.03.3-3.el7 docker-ce-stable
docker-ce.x86_64 3:19.03.2-3.el7 docker-ce-stable
docker-ce.x86_64 3:19.03.1-3.el7 docker-ce-stable
....
```
##### 下载到指定文件夹
```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
```
```bash
Dependencies Resolved
====================================================================================================================================================================================
Package Arch Version Repository Size
====================================================================================================================================================================================
Installing:
docker-ce x86_64 24.0.4-1.el7 docker 25 M
Installing for dependencies:
container-selinux noarch 24.0.4-1.el7 extras 39 k
containerd.io x86_64 24.0.4-1.el7 docker 23 M
docker-ce-cli x86_64 24.0.4-1.el7 docker 40 M
Transaction Summary
====================================================================================================================================================================================
Install 1 Package (+3 Dependent packages)
Total download size: 87 M
Installed size: 363 M
Background downloading packages, then exiting:
(1/4): container-selinux-24.0.4-1.el7.noarch.rpm | 39 kB 00:00:00
(2/4): containerd.io-24.0.4-1.el7.x86_64.rpm | 23 MB 00:00:00
(3/4): docker-ce-24.0.4-1.el7.x86_64.rpm | 25 MB 00:00:00
(4/4): docker-ce-cli-24.0.4-1.el7.x86_64.rpm | 40 MB 00:00:00
------------------------------------------------------------------------------------------------------------------------------------------------------------------------------------
Total 118 MB/s | 87 MB 00:00:00
exiting because "Download Only" specified
```
##### 复制到目标服务器之后进入文件夹安装:C-N
* 离线安装时,必须使用 rpm 命令不检查依赖的方式安装
```bash
rpm -Uvh *.rpm --nodeps --force
```
##### 锁定软件版本:C-N
**下载锁定版本软件**
可参考下文的网络源搭建
```bash
sudo yum install yum-plugin-versionlock
```
**锁定软件版本**
```bash
sudo yum versionlock add docker
```
**查看锁定列表**
```bash
sudo yum versionlock list
```
```bash
Loaded plugins: fastestmirror, versionlock
3:docker-ce-24.0.4-1.el7.*
versionlock list done
```
**锁定后无法再更新**
```bash
sudo yum install docker-ce
Loaded plugins: fastestmirror, versionlock
Loading mirror speeds from cached hostfile
Excluding 1 update due to versionlock (use "yum versionlock status" to show it)
Package 3:docker-ce-24.0.4-1.el7.x86_64 already installed and latest version
Nothing to do
```
**解锁指定软件**
```bash
sudo yum versionlock delete docker-ce
```
```bash
Loaded plugins: fastestmirror, versionlock
Deleting versionlock for: 3:docker-ce-24.0.4-1.el7.*
versionlock deleted: 1
```
**解锁所有软件**
```bash
sudo yum versionlock delete all
```
#### YUM 本地源服务器搭建安装 Docker
##### 挂载 ISO 镜像搭建本地 File 源
```bash
## 删除其他网络源
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
enabled=1
gpgcheck=0
gpgkey=file:///mnt/RPM-GPG-KEY-CentOS-7
EOF
```
```bash
## 测试刚才的本地源,安装createrepo软件
yum clean all
yum install createrepo -y
```
##### 根据本地文件搭建 BASE 网络源
```bash
## 安装apache 服务器
yum install httpd -y
## 挂载光盘
mount /dev/cdrom /mnt
## 新建centos目录
mkdir /var/www/html/base
## 复制光盘内的文件到刚才新建的目录
cp -R /mnt/Packages/* /var/www/html/base/
createrepo /var/www/html/base/
systemctl enable httpd
systemctl start httpd
```
##### 下载 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目录
mkdir /tmp/docker-ce/
## 把镜像源同步到镜像文件中
reposync -r docker-ce-stable -p /tmp/docker-ce/
```
##### 创建仓库索引
把下载的 docker-ce 文件夹复制到离线的服务器
```bash
## 把docker-ce 文件夹复制到/var/www/html/docker-ce
## 重建索引
createrepo /var/www/html/docker-ce/
```
##### YUM 客户端设置:C...N
```bash
rm -f /etc/yum.repos.d/*
cat >/etc/yum.repos.d/local_files.repo<< EOF
[local_base]
name=local_base
## 改成B服务器地址
baseurl=http://x.x.x.x/base
enabled=1
gpgcheck=0
proxy=_none_
[docker_ce]
name=docker_ce
## 改成B服务器地址
baseurl=http://x.x.x.x/docker-ce
enabled=1
gpgcheck=0
proxy=_none_
EOF
```
##### Docker 安装:C...N
```bash
sudo yum makecache fast
sudo yum install docker-ce docker-ce-cli containerd.io
sudo systemctl enable docker
```
================================================
FILE: 03_install/3.7_mac.md
================================================
## 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` 文件夹即可 (其间需要输入用户密码)。

### 3.7.3 运行
从应用中找到 Docker 图标并点击运行。

运行之后,会在右上角菜单栏看到多了一个鲸鱼图标,这个图标表明了 Docker 的运行状态。

每次点击鲸鱼图标会弹出操作菜单。

之后,你可以在终端通过命令检查安装后的 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 安装成功了。

要停止 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/)
================================================
FILE: 03_install/3.8_windows.md
================================================
## 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** 开始运行。

Docker 启动之后会在 Windows 任务栏出现鲸鱼图标。

等待片刻,当鲸鱼图标静止时,说明 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/)
================================================
FILE: 03_install/README.md
================================================
# 第三章 安装 Docker
Docker 分为 `stable` `test` 和 `nightly` 三个更新频道。
官方网站上有各种环境下的[安装指南](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)
================================================
FILE: 03_install/summary.md
================================================
## 本章小结
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)。
================================================
FILE: 04_image/4.1_pull.md
================================================
## 4.1 获取镜像
从 Docker 镜像仓库获取镜像可谓是 Docker 运作的第一步。本节将介绍如何使用 `docker pull` 命令下载镜像,以及如何理解下载过程。
### 4.1.1 docker pull 命令
从镜像仓库获取镜像的命令是 `docker pull`:
```bash
docker pull [选项] [Registry地址/]仓库名[:标签]
```
#### 镜像名称格式
Docker 镜像名称由 Registry 地址、用户名、仓库名和标签组成。其标准格式如下:
```bash
docker.io / library / ubuntu : 24.04
────┬──── ───┬─── ──┬─── ──┬──
│ │ │ │
Registry地址 用户名 仓库名 标签
(可省略) (可省略)
```
| 组成部分 | 说明 | 默认值 |
|---------|------|--------|
| Registry 地址 | 镜像仓库地址 | `docker.io` (Docker Hub)|
| 用户名 | 镜像所属用户/组织 | `library` (官方镜像)|
| 仓库名 | 镜像名称 | 必须指定 |
| 标签 | 版本标识 | `latest` |
#### 示例
```bash
## 完整格式
$ docker pull docker.io/library/ubuntu:24.04
## 省略 Registry(默认 Docker Hub)
$ docker pull library/ubuntu:24.04
## 省略 library(官方镜像)
$ docker pull ubuntu:24.04
## 省略标签(默认 latest)
$ docker pull ubuntu
## 拉取第三方镜像
$ docker pull bitnami/redis:latest
## 从其他 Registry 拉取
$ docker pull ghcr.io/username/myapp:v1.0
```
---
### 4.1.2 下载过程解析
当我们执行 `docker pull` 命令时,Docker 会输出详细的下载进度。让我们以 `ubuntu:24.04` 为例来解析这些信息。
```bash
$ docker pull ubuntu:24.04
24.04: Pulling from library/ubuntu
92dc2a97ff99: Pull complete
be13a9d27eb8: Pull complete
c8299583700a: Pull complete
Digest: sha256:4bc3ae6596938cb0d9e5ac51a1152ec9dcac2a1c50829c74abd9c4361e321b26
Status: Downloaded newer image for ubuntu:24.04
docker.io/library/ubuntu:24.04
```
#### 输出解读
| 输出内容 | 说明 |
|---------|------|
| `Pulling from library/ubuntu` | 正在从官方 ubuntu 仓库拉取 |
| `92dc2a97ff99: Pull complete` | 各层的下载状态 (显示层 ID 前 12 位)|
| `Digest: sha256:...` | 镜像内容的唯一摘要 |
| `docker.io/library/ubuntu:24.04` | 镜像的完整名称 |
#### 分层下载
从输出可以看到,镜像是 **分层下载** 的:
```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` 命令支持多种选项来满足不同的下载需求,例如下载所有标签、指定平台架构等。
| 选项 | 说明 | 示例 |
|------|------|------|
| `--all-tags, -a` | 拉取所有标签 | `docker pull -a ubuntu` |
| `--platform` | 指定平台架构 | `docker pull --platform linux/arm64 nginx` |
| `--quiet, -q` | 静默模式 | `docker pull -q nginx` |
#### 指定平台
在 Apple Silicon Mac 上拉取 x86 镜像:
```bash
$ 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"
...
root@e7009c6ce357:/# exit
```
**参数说明**:
| 参数 | 说明 |
|------|------|
| `-it` | 交互式终端模式 |
| `--rm` | 退出后自动删除容器 |
| `bash` | 启动命令 |
> 💡 `docker run` 在需要时会自动 `pull` 镜像,因此通常不需要单独执行 `docker pull`。
---
### 4.1.5 镜像加速
从 Docker Hub 下载可能较慢。可以配置镜像加速器:
```json
// /etc/docker/daemon.json (Linux)
// ~/.docker/daemon.json (Docker Desktop)
{
"registry-mirrors": [
"https://your-accelerator-url"
]
}
```
配置后重启 Docker:
```bash
$ sudo systemctl restart docker # Linux
## 或在 Docker Desktop 中重启
```
详见[镜像加速器](../03_install/3.9_mirror.md)章节。
---
### 4.1.6 验证镜像完整性
为了确保下载的镜像没有被篡改且内容一致,我们可以校验镜像的摘要 (Digest)。
#### 查看镜像摘要
```bash
$ docker images --digests ubuntu
REPOSITORY TAG DIGEST IMAGE ID
ubuntu 24.04 sha256:4bc3ae6596938cb0d9e5ac51a1152ec9dcac2a1c50829c74abd9c4361e321b26 ca2b0f26964c
```
#### 使用摘要拉取
用摘要拉取可确保获取完全相同的镜像:
```bash
$ docker pull ubuntu@sha256:4bc3ae6596938cb0d9e5ac51a1152ec9dcac2a1c50829c74abd9c4361e321b26
```
> 笔者建议:生产环境使用摘要而非标签,因为标签可能被覆盖,摘要则是不可变的。
---
### 4.1.7 常见问题
在使用 `docker pull` 过程中,可能会遇到下载速度慢、镜像不存在或磁盘空间不足等问题。以下是一些常见问题的排查思路。
#### Q:下载速度很慢
1. 配置镜像加速器
2. 检查网络连接
3. 尝试拉取更小的镜像版本 (如 `alpine` 变体)
#### Q:提示镜像不存在
```bash
Error: pull access denied, repository does not exist
```
可能原因:
- 镜像名拼写错误
- 私有镜像未登录 (需要 `docker login`)
- 镜像确实不存在
#### Q:磁盘空间不足
```bash
## 清理未使用的镜像
$ docker image prune
## 清理所有未使用资源
$ docker system prune
```
---
================================================
FILE: 04_image/4.2_list.md
================================================
## 4.2 列出镜像
在下载了镜像后,我们可以使用 `docker image ls` 命令列出本地主机上的镜像。
### 4.2.1 基本用法
查看本地已下载的镜像:
```bash
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED SIZE
redis latest 5f515359c7f8 5 days ago 183MB
nginx latest 05a60462f8ba 5 days ago 181MB
ubuntu 24.04 329ed837d508 3 days ago 78MB
ubuntu noble 329ed837d508 3 days ago 78MB
```
> 💡 `docker images` 是 `docker image ls` 的简写,两者等效。
---
### 4.2.2 输出字段说明
`docker image ls` 命令默认输出的列表包含仓库名、标签、镜像 ID、创建时间和占用空间等信息。
| 字段 | 说明 |
|------|------|
| **REPOSITORY** | 仓库名 |
| **TAG** | 标签 (版本)|
| **IMAGE ID** | 镜像唯一标识 (短 ID,前 12 位)|
| **CREATED** | 创建时间 |
| **SIZE** | 本地占用空间 |
#### 同一镜像多个标签
注意上面的 `ubuntu:24.04` 和 `ubuntu:noble` 拥有相同的 IMAGE ID——它们是同一个镜像的不同标签,只占用一份存储空间。
---
### 4.2.3 理解镜像大小
Docker 镜像的大小可能与我们通常理解的文件大小有所不同,这涉及到分层存储的概念。
#### 本地大小 vs Hub 显示大小
| 位置 | 显示大小 | 说明 |
|------|---------|------|
| Docker Hub | 29MB | 压缩后的网络传输大小 |
| docker image ls | 78MB | 本地解压后的实际大小 |
#### 实际磁盘占用
由于镜像是分层存储,不同镜像可能共享相同的层:
```bash
ubuntu:24.04 nginx:latest redis:latest
│ │ │
└───────┬───────┘ │
▼ │
共享基础层 ◄───────────────────┘
```
因此,`docker image ls` 中各镜像大小之和 > 实际磁盘占用。
#### 查看实际空间占用
```bash
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 15 3 2.5GB 1.8GB (72%)
Containers 5 2 100MB 80MB (80%)
Local Volumes 8 2 500MB 400MB (80%)
Build Cache 0 0 0B 0B
```
---
### 4.2.4 过滤镜像
随着本地镜像数量的增加,我们需要更有效的方式来查找特定的镜像。Docker 提供了多种过滤方式。
#### 按仓库名过滤
```bash
## 列出所有 ubuntu 镜像
$ docker images ubuntu
REPOSITORY TAG IMAGE ID SIZE
ubuntu 24.04 329ed837d508 78MB
ubuntu noble 329ed837d508 78MB
ubuntu 22.04 a1b2c3d4e5f6 72MB
```
#### 按仓库名和标签过滤
```bash
$ docker images ubuntu:24.04
REPOSITORY TAG IMAGE ID SIZE
ubuntu 24.04 329ed837d508 78MB
```
#### 使用过滤器 --filter
| 过滤条件 | 说明 | 示例 |
|---------|------|------|
| `dangling=true` | 虚悬镜像 | `-f dangling=true` |
| `before=镜像` | 在某镜像之前创建 | `-f before=nginx:latest` |
| `since=镜像` | 在某镜像之后创建 | `-f since=nginx:latest` |
| `label=key=value` | 按 LABEL 过滤 | `-f label=version=1.0` |
| `reference=pattern` | 按名称模式 | `-f reference='*:latest'` |
```bash
## 列出 nginx 之后创建的镜像
$ docker images -f since=nginx:latest
## 列出所有带 latest 标签的镜像
$ docker images -f reference='*:latest'
## 列出带特定 LABEL 的镜像
$ docker images -f label=maintainer=example@email.com
```
---
### 4.2.5 虚悬镜像
在镜像列表里,你可能会看到一些仓库名和标签都为 `<none>` 的镜像,这类镜像被称为虚悬镜像。
#### 什么是虚悬镜像
仓库名和标签都显示为 `<none>` 的镜像:
```bash
$ docker images
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
```
会显示很多无标签镜像——这些是构建过程中产生的中间层,被其他镜像依赖。
> ⚠️ 不要删除中间层镜像。它们是其他镜像的依赖,删除会导致上层镜像无法使用。删除顶层镜像时会自动清理不再需要的中间层。
---
### 4.2.7 格式化输出
为了配合脚本使用或展示更关注的信息,我们可以使用 `--format` 参数来自定义输出格式。
#### 只输出 ID
```bash
$ docker images -q
5f515359c7f8
05a60462f8ba
329ed837d508
```
常用于配合其他命令:
```bash
## 删除所有镜像
$ docker rmi $(docker images -q)
## 删除所有 redis 镜像
$ docker rmi $(docker images -q redis)
```
#### 显示完整 ID
```bash
$ docker images --no-trunc
```
#### 显示摘要
```bash
$ docker images --digests
REPOSITORY TAG DIGEST IMAGE ID
nginx latest sha256:b4f0e0bdeb5... e43d811ce2f4
```
#### 自定义格式
使用 Go 模板语法自定义输出:
```bash
## 只显示 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
nginx latest 181MB
ubuntu 24.04 78MB
```
#### 可用模板字段
| 字段 | 说明 |
|------|------|
| `.ID` | 镜像 ID |
| `.Repository` | 仓库名 |
| `.Tag` | 标签 |
| `.Digest` | 摘要 |
| `.CreatedSince` | 创建后经过的时间 |
| `.CreatedAt` | 创建时间 |
| `.Size` | 大小 |
---
### 4.2.8 常用命令组合
```bash
## 列出所有镜像及其大小,按大小排序(需要系统 sort 命令)
$ docker images --format "{{.Size}}\t{{.Repository}}:{{.Tag}}" | sort -h
## 查找大于 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
```
---
================================================
FILE: 04_image/4.3_rm.md
================================================
## 4.3 删除本地镜像
当不再需要某个镜像时,我们可以将其删除以释放存储空间。本节介绍删除镜像的常用方法。
### 4.3.1 基本用法
使用 `docker image rm` 删除本地镜像:
```bash
$ docker image rm [选项] <镜像1> [<镜像2> ...]
```
> 💡 `docker rmi` 是 `docker image rm` 的简写,两者等效。
---
### 4.3.2 镜像标识方式
删除镜像时,可以使用多种方式指定镜像:
| 方式 | 说明 | 示例 |
|------|------|------|
| **短 ID** | ID 的前几位 (通常 3-4 位)| `docker rmi 501` |
| **长 ID** | 完整的镜像 ID | `docker rmi 501ad78535f0...` |
| **镜像名:标签** | 仓库名和标签 | `docker rmi redis:alpine` |
| **镜像摘要** | 精确的内容摘要 | `docker rmi nginx@sha256:...` |
#### 使用短 ID 删除
```bash
$ docker image ls
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
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**
```bash
$ docker rmi redis:alpine
Untagged: redis:alpine
Untagged: redis@sha256:f1ed3708f538b537eb9c2a7dd50dc90a706f7debd7e1196c9264edeea521a86d
Deleted: sha256:501ad78535f015d88872e13fa87a828425117e3d28075d0c117932b05bf189b7
Deleted: sha256:96167737e29ca8e9d74982ef2a0dda76ed7b430da55e321c071f0dbff8c2899b
Deleted: sha256:32770d1dcf835f192cafd6b9263b7b597a1778a403a109e2cc2ee866f74adf23
```
#### Untagged vs Deleted
| 操作 | 含义 |
|------|------|
| **Untagged** | 移除镜像的标签 |
| **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):没有标签的镜像,通常是旧版本被新版本覆盖后产生的
```bash
## 查看虚悬镜像
$ docker images -f dangling=true
## 删除虚悬镜像
$ docker image prune
## 不提示确认
$ docker image prune -f
```
#### 删除所有未使用的镜像
```bash
## 删除所有没有被容器使用的镜像
$ docker image prune -a
## 保留最近 24 小时的
$ docker image prune -a --filter "until=24h"
```
#### 按条件删除
```bash
## 删除所有 redis 镜像
$ docker rmi $(docker images -q redis)
## 删除 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
Error: conflict: unable to remove repository reference "nginx"
(must force) - container abc123 is using its referenced image
```
**解决方案**:
```bash
## 方案1:先删除依赖的容器
$ docker rm abc123
$ docker rmi nginx
## 方案2:强制删除镜像(容器仍可运行,但无法再创建新容器)
$ docker rmi -f nginx
```
#### 原因二:多个标签指向同一镜像
```bash
$ docker images
REPOSITORY TAG IMAGE ID
ubuntu 24.04 ca2b0f26964c
ubuntu latest ca2b0f26964c # 同一个镜像
$ docker rmi ubuntu:24.04
Untagged: ubuntu:24.04
## 只是移除标签,镜像仍存在(因为还有 ubuntu:latest 指向它)
```
当同一个镜像有多个标签时,`docker rmi` 只是删除指定的标签,不会删除镜像本身。
#### 原因三:被其他镜像依赖:中间层
```bash
$ docker rmi some_base_image
Error: image has dependent child images
```
中间层镜像被其他镜像依赖,无法删除。需要先删除依赖它的镜像。
---
### 4.3.6 常用过滤条件
| 过滤条件 | 说明 | 示例 |
|---------|------|------|
| `dangling=true` | 虚悬镜像 | `-f dangling=true` |
| `before=镜像` | 在某镜像之前 | `-f before=mongo:3.2` |
| `since=镜像` | 在某镜像之后 | `-f since=mongo:3.2` |
| `label=key=value` | 按标签过滤 | `-f label=version=1.0` |
| `reference=pattern` | 按名称模式 | `-f reference='*:latest'` |
---
### 4.3.7 清理策略
针对不同的环境 (开发环境 vs 生产环境),我们应该采用不同的镜像清理策略。
#### 开发环境
```bash
## 定期清理虚悬镜像
$ docker image prune -f
## 一键清理所有未使用资源
$ docker system prune -a
```
#### CI/CD 环境
```bash
## 只保留最近使用的镜像
$ docker image prune -a --filter "until=72h" -f
```
#### 查看空间占用
```bash
$ docker system df
TYPE TOTAL ACTIVE SIZE RECLAIMABLE
Images 15 3 2.5GB 1.8GB (72%)
Containers 5 2 100MB 80MB (80%)
Local Volumes 8 2 500MB 400MB (80%)
Build Cache 0 0 0B 0B
```
---
================================================
FILE: 04_image/4.4_commit.md
================================================
## 4.4 利用 commit 理解镜像构成
> 注意:如果您是初学者,您可以暂时跳过后面的内容,直接学习[容器](../05_container/)一节。
注意:`docker commit` 命令除了学习之外,还有一些特殊的应用场合,比如被入侵后保存现场等。但是,不要使用 `docker commit` 定制镜像,定制镜像应该使用 `Dockerfile` 来完成。如果你想要定制镜像请查看下一小节。
镜像是容器的基础,每次执行 `docker run` 的时候都会指定哪个镜像作为容器运行的基础。在之前的例子中,我们所使用的都是来自于 Docker Hub 的镜像。直接使用这些镜像是可以满足一定的需求,而当这些镜像无法直接满足需求时,我们就需要定制这些镜像。接下来的几节就将讲解如何定制镜像。
回顾一下之前我们学到的知识,镜像是多层存储,每一层是在前一层的基础上进行的修改;而容器同样也是多层存储,是在以镜像为基础层,在其基础上加一层作为容器运行时的存储层。
现在让我们以定制一个 Web 服务器为例子,来讲解镜像是如何构建的。
```bash
$ docker run --name webserver -d -p 80:80 nginx
```
这条命令会用 `nginx` 镜像启动一个容器,命名为 `webserver`,并且映射了 80 端口,这样我们可以用浏览器去访问这个 `nginx` 服务器。
如果是在本机运行的 Docker,那么可以直接访问:`http://localhost`,如果是在虚拟机、云服务器上安装的 Docker,则需要将 `localhost` 换为虚拟机地址或者实际云服务器地址。
直接用浏览器访问的话,我们会看到默认的 Nginx 欢迎页面。

现在,假设我们非常不喜欢这个欢迎页面,我们希望改成欢迎 Docker 的文字,我们可以使用 `docker exec` 命令进入容器,修改其内容。
```bash
$ docker exec -it webserver bash
root@3729b97e8226:/# echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
root@3729b97e8226:/# exit
exit
```
我们以交互式终端方式进入 `webserver` 容器,并执行了 `bash` 命令,也就是获得一个可操作的 Shell。
然后,我们用 `<h1>Hello, Docker!</h1>` 覆盖了 `/usr/share/nginx/html/index.html` 的内容。
现在我们再刷新浏览器的话,会发现内容被改变了。

我们修改了容器的文件,也就是改动了容器的存储层。我们可以通过 `docker diff` 命令看到具体的改动。
```bash
$ docker diff webserver
C /root
A /root/.bash_history
C /run
C /usr
C /usr/share
C /usr/share/nginx
C /usr/share/nginx/html
C /usr/share/nginx/html/index.html
C /var
C /var/cache
C /var/cache/nginx
A /var/cache/nginx/client_temp
A /var/cache/nginx/fastcgi_temp
A /var/cache/nginx/proxy_temp
A /var/cache/nginx/scgi_temp
A /var/cache/nginx/uwsgi_temp
```
现在我们定制好了变化,我们希望能将其保存下来形成镜像。
要知道,当我们运行一个容器的时候 (如果不使用卷的话),我们做的任何文件修改都会被记录于容器存储层里。而 Docker 提供了一个 `docker commit` 命令,可以将容器的存储层保存下来成为镜像。换句话说,就是在原有镜像的基础上,再叠加上容器的存储层,并构成新的镜像。以后我们运行这个新镜像的时候,就会拥有原有容器最后的文件变化。
`docker commit` 的语法格式为:
```bash
docker commit [选项] <容器ID或容器名> [<仓库名>[:<标签>]]
```
我们可以用下面的命令将容器保存为镜像:
```bash
$ docker commit \
--author "Tao Wang <twang2218@gmail.com>" \
--message "修改了默认网页" \
webserver \
nginx:v2
sha256:07e33465974800ce65751acc279adc6ed2dc5ed4e0838f8b86f0c87aa1795214
```
其中 `--author` 是指定修改的作者,而 `--message` 则是记录本次修改的内容。这点和 `git` 版本控制相似,不过这里这些信息可以省略留空。
我们可以在 `docker image ls` 中看到这个新定制的镜像:
```bash
$ docker image ls nginx
REPOSITORY TAG IMAGE ID CREATED SIZE
nginx v2 07e334659748 9 seconds ago 181.5 MB
nginx 1.27 05a60462f8ba 12 days ago 181.5 MB
nginx latest e43d811ce2f4 4 weeks ago 181.5 MB
```
我们还可以用 `docker history` 具体查看镜像内的历史记录,如果比较 `nginx:latest` 的历史记录,我们会发现新增了我们刚刚提交的这一层。
```bash
$ docker history nginx:v2
IMAGE CREATED CREATED BY SIZE COMMENT
07e334659748 54 seconds ago nginx -g daemon off; 95 B 修改了默认网页
e43d811ce2f4 4 weeks ago /bin/sh -c #(nop) CMD ["nginx" "-g" "daemon 0 B
<missing> 4 weeks ago /bin/sh -c #(nop) EXPOSE 443/tcp 80/tcp 0 B
<missing> 4 weeks ago /bin/sh -c ln -sf /dev/stdout /var/log/nginx/ 22 B
<missing> 4 weeks ago /bin/sh -c apt-key adv --keyserver hkp://pgp. 58.46 MB
<missing> 4 weeks ago /bin/sh -c #(nop) ENV NGINX_VERSION=1.27.0-1 0 B
<missing> 4 weeks ago /bin/sh -c #(nop) MAINTAINER NGINX Docker Ma 0 B
<missing> 4 weeks ago /bin/sh -c #(nop) CMD ["/bin/bash"] 0 B
<missing> 4 weeks ago /bin/sh -c #(nop) ADD file:23aa4f893e3288698c 123 MB
```
新的镜像定制好后,我们可以来运行这个镜像。
```bash
docker run --name web2 -d -p 81:80 nginx:v2
```
这里我们命名为新的服务为 `web2`,并且映射到 `81` 端口。访问 `http://localhost:81` 看到结果,其内容应该和之前修改后的 `webserver` 一样。
至此,我们第一次完成了定制镜像,使用的是 `docker commit` 命令,手动操作给旧的镜像添加了新的一层,形成新的镜像,对镜像多层存储应该有了更直观的感觉。
### 4.4.1 慎用 `docker commit`
使用 `docker commit` 命令虽然可以比较直观的帮助理解镜像分层存储的概念,但是实际环境中并不会这样使用。
首先,如果仔细观察之前的 `docker diff webserver` 的结果,你会发现除了真正想要修改的 `/usr/share/nginx/html/index.html` 文件外,由于命令的执行,还有很多文件被改动或添加了。这还仅仅是最简单的操作,如果是安装软件包、编译构建,那会有大量的无关内容被添加进来,将会导致镜像极为臃肿。
此外,使用 `docker commit` 意味着所有对镜像的操作都是黑箱操作,生成的镜像也被称为 **黑箱镜像**,换句话说,就是除了制作镜像的人知道执行过什么命令、怎么生成的镜像,别人根本无从得知。而且,即使是这个制作镜像的人,过一段时间后也无法记清具体的操作。这种黑箱镜像的维护工作是非常痛苦的。
而且,回顾之前提及的镜像所使用的分层存储的概念,除当前层外,之前的每一层都是不会发生改变的,换句话说,任何修改的结果仅仅是在当前层进行标记、添加、修改,而不会改动上一层。如果使用 `docker commit` 制作镜像,以及后期修改的话,每一次修改都会让镜像更加臃肿一次,所删除的上一层的东西并不会丢失,会一直如影随形的跟着这个镜像,即使根本无法访问到。这会让镜像更加臃肿。
================================================
FILE: 04_image/4.5_build.md
================================================
## 4.5 使用 Dockerfile 定制镜像
从刚才的 `docker commit` 的学习中,我们可以了解到,镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这个脚本来构建、定制镜像,那么之前提及的无法重复的问题、镜像构建透明性的问题、体积的问题就都会解决。这个脚本就是 Dockerfile。
Dockerfile 是一个文本文件,其内包含了一条条的 **指令 (Instruction)**,每一条指令构建一层,因此每一条指令的内容,就是描述该层应当如何构建。
### 4.5.1 使用 docker init 快速创建:推荐
Docker 提供了 `docker init` 命令,可以根据项目类型自动生成 Dockerfile、.dockerignore 和 compose.yaml 文件:
```bash
$ docker init
```
该命令会交互式地询问项目类型 (如 Go、Node.js、Python、Rust 等),并生成符合最佳实践的配置文件。对于新项目,这是推荐的起步方式。
### 4.5.2 手动创建 Dockerfile
还以之前定制 `nginx` 镜像为例,这次我们使用 Dockerfile 来定制。
在一个空白目录中,建立一个文本文件,并命名为 `Dockerfile`:
```bash
$ mkdir mynginx
$ cd mynginx
$ touch Dockerfile
```
其内容为:
```docker
FROM nginx
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
```
这个 Dockerfile 很简单,一共就两行。涉及到了两条指令,`FROM` 和 `RUN`。
### 4.5.3 FROM 指定基础镜像
所谓定制镜像,那一定是以一个镜像为基础,在其上进行定制。就像我们之前运行了一个 `nginx` 镜像的容器,再进行修改一样,基础镜像是必须指定的。而 `FROM` 就是指定 **基础镜像**,因此一个 `Dockerfile` 中 `FROM` 是必备的指令,并且必须是第一条指令。
在 [Docker Hub](https://hub.docker.com/search?q=&type=image&image_filter=official) 上有非常多的高质量的官方镜像,有可以直接拿来使用的服务类的镜像,如 [`nginx`](https://hub.docker.com/_/nginx/)、[`redis`](https://hub.docker.com/_/redis/)、[`mongo`](https://hub.docker.com/_/mongo/)、[`mysql`](https://hub.docker.com/_/mysql/)、[`httpd`](https://hub.docker.com/_/httpd/)、[`php`](https://hub.docker.com/_/php/)、[`tomcat`](https://hub.docker.com/_/tomcat/) 等;也有一些方便开发、构建、运行各种语言应用的镜像,如 [`node`](https://hub.docker.com/_/node)、[`openjdk`](https://hub.docker.com/_/openjdk/)、[`python`](https://hub.docker.com/_/python/)、[`ruby`](https://hub.docker.com/_/ruby/)、[`golang`](https://hub.docker.com/_/golang/) 等。可以在其中寻找一个最符合我们最终目标的镜像为基础镜像进行定制。
如果没有找到对应服务的镜像,官方镜像中还提供了一些更为基础的操作系统镜像,如 [`ubuntu`](https://hub.docker.com/_/ubuntu/)、[`debian`](https://hub.docker.com/_/debian/)、[`centos`](https://hub.docker.com/_/centos/)、[`fedora`](https://hub.docker.com/_/fedora/)、[`alpine`](https://hub.docker.com/_/alpine/) 等,这些操作系统的软件库为我们提供了更广阔的扩展空间。
除了选择现有镜像为基础镜像外,Docker 还存在一个特殊的镜像,名为 `scratch`。这个镜像是虚拟的概念,并不实际存在,它表示一个空白的镜像。
```docker
FROM scratch
...
```
如果你以 `scratch` 为基础镜像的话,意味着你不以任何镜像为基础,接下来所写的指令将作为镜像第一层开始存在。
不以任何系统为基础,直接将可执行文件复制进镜像的做法并不罕见,对于 Linux 下静态编译的程序来说,并不需要有操作系统提供运行时支持,所需的一切库都已经在可执行文件里了,因此直接 `FROM scratch` 会让镜像体积更加小巧。使用 [Go 语言](https://golang.google.cn/)开发的应用很多会使用这种方式来制作镜像,这也是有人认为 Go 是特别适合容器微服务架构的语言的原因之一。
### 4.5.4 RUN 执行命令
`RUN` 指令是用来执行命令行命令的。由于命令行的强大能力,`RUN` 指令在定制镜像时是最常用的指令之一。其格式有两种:
* *shell* 格式:`RUN <命令>`,就像直接在命令行中输入的命令一样。刚才写的 Dockerfile 中的 `RUN` 指令就是这种格式。
```docker
RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
```
* *exec* 格式:`RUN [“可执行文件”, “参数1”, “参数2”]`,这更像是函数调用中的格式。
Dockerfile 中每一个指令都会建立一层,`RUN` 也不例外。每一个 `RUN` 的行为,就和刚才我们手工建立镜像的过程一样:新建立一层,在其上执行这些命令,执行结束后,`commit` 这一层的修改,构成新的镜像。
> **注意**
>
> 每一个 `RUN` 指令都会产生一个新的镜像层。为了减少镜像体积和层数,我们通常会将多个命令合并到一个 `RUN` 指令中执行。
>
> 更多关于 `RUN` 指令的详细用法、最佳实践 (如清理缓存、使用 pipefail 等) 及 `Union FS` 的层数限制等内容,请参阅 **[第七章 Dockerfile 指令详解](../07_dockerfile/README.md)** 中的 **[RUN 指令](../07_dockerfile/7.1_run.md)** 小节。
要想编写优秀的 `Dockerfile`,必须了解每一条指令的作用和副作用。在 **[第七章 Dockerfile 指令详解](../07_dockerfile/README.md)** 中,我们将对 `COPY`,`ADD`,`CMD`,`ENTRYPOINT` 等指令进行详细讲解。
### 4.5.5 构建镜像
好了,让我们再回到之前定制的 nginx 镜像的 Dockerfile 来。现在我们明白了这个 Dockerfile 的内容,那么让我们来构建这个镜像吧。
在 `Dockerfile` 文件所在目录执行:
```bash
$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
Step 1 : FROM nginx
---> e43d811ce2f4
Step 2 : RUN echo '<h1>Hello, Docker!</h1>' > /usr/share/nginx/html/index.html
---> Running in 9cdc27646c7b
---> 44aa4490ce2c
Removing intermediate container 9cdc27646c7b
Successfully built 44aa4490ce2c
```
从命令的输出结果中,我们可以清晰的看到镜像的构建过程。在 `Step 2` 中,如同我们之前所说的那样,`RUN` 指令启动了一个容器 `9cdc27646c7b`,执行了所要求的命令,并最后提交了这一层 `44aa4490ce2c`,随后删除了所用到的这个容器 `9cdc27646c7b`。
这里我们使用了 `docker build` 命令进行镜像构建。其格式为:
```bash
docker build [选项] <上下文路径/URL/->
```
在这里我们指定了最终镜像的名称 `-t nginx:v3`,构建成功后,我们可以像之前运行 `nginx:v2` 那样来运行这个镜像,其结果会和 `nginx:v2` 一样。
### 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 引擎变得轻而易举。
当我们进行镜像构建的时候,并非所有定制都会通过 `RUN` 指令完成,经常会需要将一些本地文件复制进镜像,比如通过 `COPY` 指令、`ADD` 指令等。而 `docker build` 命令构建镜像,其实并非在本地构建,而是在服务端,也就是 Docker 引擎中构建的。那么在这种客户端/服务端的架构中,如何才能让服务端获得本地文件呢?
这就引入了上下文的概念。当构建的时候,用户会指定构建镜像上下文的路径,`docker build` 命令得知这个路径后,会将路径下的所有内容打包,然后上传给 Docker 引擎。这样 Docker 引擎收到这个上下文包后,展开就会获得构建镜像所需的一切文件。
如果在 `Dockerfile` 中这么写:
```docker
COPY ./package.json /app/
```
这并不是要复制执行 `docker build` 命令所在的目录下的 `package.json`,也不是复制 `Dockerfile` 所在目录下的 `package.json`,而是复制 **上下文 (context)** 目录下的 `package.json`。
因此,`COPY` 这类指令中的源文件的路径都是*相对路径*。这也是初学者经常会问的为什么 `COPY ../package.json /app` 或者 `COPY /opt/xxxx /app` 无法工作的原因,因为这些路径已经超出了上下文的范围,Docker 引擎无法获得这些位置的文件。如果真的需要那些文件,应该将它们复制到上下文目录中去。
现在就可以理解刚才的命令 `docker build -t nginx:v3 .` 中的这个 `.`,实际上是在指定上下文的目录,`docker build` 命令会将该目录下的内容打包交给 Docker 引擎以帮助构建镜像。
如果观察 `docker build` 输出,我们其实已经看到了这个发送上下文的过程:
```bash
$ docker build -t nginx:v3 .
Sending build context to Docker daemon 2.048 kB
...
```
理解构建上下文对于镜像构建是很重要的,避免犯一些不应该的错误。比如有些初学者在发现 `COPY /opt/xxxx /app` 不工作后,于是干脆将 `Dockerfile` 放到了硬盘根目录去构建,结果发现 `docker build` 执行后,在发送一个几十 GB 的东西,极为缓慢而且很容易构建失败。那是因为这种做法是在让 `docker build` 打包整个硬盘,这显然是使用错误。
一般来说,应该会将 `Dockerfile` 置于一个空目录下,或者项目根目录下。如果该目录下没有所需文件,那么应该把所需文件复制一份过来。如果目录下有些东西确实不希望构建时传给 Docker 引擎,那么可以用 `.gitignore` 一样的语法写一个 `.dockerignore`,该文件是用于剔除不需要作为上下文传递给 Docker 引擎的。
那么为什么会有人误以为 `.` 是指定 `Dockerfile` 所在目录呢?这是因为在默认情况下,如果不额外指定 `Dockerfile` 的话,会将上下文目录下的名为 `Dockerfile` 的文件作为 Dockerfile。
这只是默认行为,实际上 `Dockerfile` 的文件名并不要求必须为 `Dockerfile`,而且并不要求必须位于上下文目录中,比如可以用 `-f ../Dockerfile.php` 参数指定某个文件作为 `Dockerfile`。
当然,一般大家习惯性的会使用默认的文件名 `Dockerfile`,以及会将其置于镜像构建上下文目录中。
### 4.5.7 其它 `docker build` 的用法
#### 直接用 Git repo 进行构建
或许你已经注意到了,`docker build` 还支持从 URL 构建,比如可以直接从 Git repo 中构建:
```bash
## $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
Step 1/3 : FROM scratch
--->
Step 2/3 : COPY hello /
---> ac779757d46e
Step 3/3 : CMD ["/hello"]
---> Running in d2a513a760ed
Removing intermediate container d2a513a760ed
---> 038ad4142d2b
Successfully built 038ad4142d2b
```
这行命令指定了构建所需的 Git repo,并且指定分支为 `master`,构建目录为 `/amd64/hello-world/`,然后 Docker 就会自己去 `git clone` 这个项目、切换到指定分支、并进入到指定目录后开始构建。
#### 用给定的 tar 压缩包构建
```bash
$ docker build http://server/context.tar.gz
```
如果所给出的 URL 不是个 Git repo,而是个 `tar` 压缩包,那么 Docker 引擎会下载这个包,并自动解压缩,以其作为上下文,开始构建。
#### 从标准输入中读取 Dockerfile 进行构建
```bash
docker build - < Dockerfile
```
或
```bash
cat Dockerfile | docker build -
```
如果标准输入传入的是文本文件,则将其视为 `Dockerfile`,并开始构建。这种形式由于直接从标准输入中读取 Dockerfile 的内容,它没有上下文,因此不可以像其他方法那样可以将本地文件 `COPY` 进镜像之类的事情。
#### 从标准输入中读取上下文压缩包进行构建
```bash
$ docker build - < context.tar.gz
```
如果发现标准输入的文件格式是 `gzip`、`bzip2` 以及 `xz` 的话,将会使其为上下文压缩包,直接将其展开,将里面视为上下文,并开始构建。
================================================
FILE: 04_image/4.6_other.md
================================================
## 4.6 其它制作镜像的方式
除了标准的使用 `Dockerfile` 生成镜像的方法外,由于各种特殊需求和历史原因,还提供了一些其它方法用以生成镜像。
### 4.6.1 从 rootfs 压缩包导入
格式:`docker import [选项] <文件>|<URL>|- [<仓库名>[:<标签>]]`
压缩包可以是本地文件、远程 Web 文件,甚至是从标准输入中得到。压缩包将会在镜像 `/` 目录展开,并直接作为镜像第一层提交。
比如我们想要创建一个 [OpenVZ](https://openvz.org) 的 Ubuntu 16.04 [模板](https://wiki.openvz.org/Download/template/precreated)的镜像:
```bash
$ docker import \
http://download.openvz.org/template/precreated/ubuntu-16.04-x86_64.tar.gz \
openvz/ubuntu:16.04
Downloading from http://download.openvz.org/template/precreated/ubuntu-16.04-x86_64.tar.gz
sha256:412b8fc3e3f786dca0197834a698932b9c51b69bd8cf49e100c35d38c9879213
```
这条命令自动下载了 `ubuntu-16.04-x86_64.tar.gz` 文件,并且作为根文件系统展开导入,并保存为镜像 `openvz/ubuntu:16.04`。
导入成功后,我们可以用 `docker image ls` 看到这个导入的镜像:
```bash
$ docker image ls openvz/ubuntu
REPOSITORY TAG IMAGE ID CREATED SIZE
openvz/ubuntu 16.04 412b8fc3e3f7 55 seconds ago 505MB
```
如果我们查看其历史的话,会看到描述中有导入的文件链接:
```bash
$ docker history openvz/ubuntu:16.04
IMAGE CREATED CREATED BY SIZE COMMENT
f477a6e18e98 About a minute ago 214.9 MB Imported from http://download.openvz.org/template/precreated/ubuntu-16.04-x86_64.tar.gz
```
### 4.6.2 Docker 镜像的导入和导出 `docker save` 和 `docker load`
Docker 还提供了 `docker save` 和 `docker load` 命令,用以将镜像保存为一个文件,然后传输到另一个位置上,再加载进来。这是在没有 Docker Registry 时的做法,现在已经不推荐,镜像迁移应该直接使用 Docker Registry,无论是直接使用 Docker Hub 还是使用内网私有 Registry 都可以。
#### 保存镜像
使用 `docker save` 命令可以将镜像保存为归档文件。
比如我们希望保存这个 `alpine` 镜像。
```bash
$ docker image ls alpine
REPOSITORY TAG IMAGE ID CREATED SIZE
alpine latest baa5d63471ea 5 weeks ago 4.803 MB
```
保存镜像的命令为:
```bash
$ docker save alpine -o filename
$ file filename
filename: POSIX tar archive
```
这里的 filename 可以为任意名称甚至任意后缀名,但文件的本质都是归档文件
**注意:如果同名则会覆盖 (没有警告)**
若使用 `gzip` 压缩:
```bash
$ docker save alpine | gzip > alpine-latest.tar.gz
```
然后我们将 `alpine-latest.tar.gz` 文件复制到了到了另一个机器上,可以用下面这个命令加载镜像:
```bash
$ docker load -i alpine-latest.tar.gz
Loaded image: alpine:latest
```
如果我们结合这两个命令以及 `ssh` 甚至 `pv` 的话,利用 Linux 强大的管道,我们可以写一个命令完成从一个机器将镜像迁移到另一个机器,并且带进度条的功能:
```bash
docker save <镜像名> | bzip2 | pv | ssh <用户名>@<主机名> 'cat | docker load'
```
================================================
FILE: 04_image/4.7_internal.md
================================================
## 4.7 实现原理
Docker 镜像是怎么实现增量的修改和维护的?为什么容器启动如此之快?这一切都归功于 Docker 的镜像分层存储设计。
### 4.7.1 镜像与分层存储
在之前的章节中,我们一直强调镜像包含操作系统完整的 `root` 文件系统,其体积往往是庞大的。因此在 Docker 设计时,就充分利用 **Union FS** 的技术,将其设计为分层存储的架构。
Docker 镜像并不是一个单纯的文件,而是由一组文件系统叠加构成的。
最底层的镜像称为 **基础镜像 (Base Image)**,通常是各种 Linux 发行版的 root 文件系统,如 Ubuntu、Debian、CentOS 等。
当我们在基础镜像之上构建新的镜像时 (例如安装了 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-Write,CoW)** 策略。
* **删除文件**:当容器删除某个文件时,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 等文件系统的具体实现原理,包括 WorkDir、UpperDir、LowerDir 等底层细节,请阅读 **[第十二章 底层实现](../12_implementation/README.md)** 中的 **[联合文件系统](../12_implementation/12.4_ufs.md)** 章节。
================================================
FILE: 04_image/README.md
================================================
# 第四章 使用镜像
在之前的介绍中,我们知道镜像是 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`)时,其元数据能够与镜像数据一并完好地管理于本地存储系统中,为供应链安全验证补齐了最后一块拼图。
================================================
FILE: 04_image/demo/buildkit/Dockerfile
================================================
FROM node:alpine as builder
WORKDIR /app
COPY package.json /app/
RUN npm i --registry=https://registry.npmmirror.com \
&& rm -rf ~/.npm
COPY src /app/src
RUN npm run build
FROM nginx:alpine
COPY --from=builder /app/dist /app/dist
================================================
FILE: 04_image/demo/buildkit/Dockerfile.buildkit
================================================
# syntax = docker/dockerfile:experimental
FROM node:alpine as builder
WORKDIR /app
COPY package.json /app/
RUN --mount=type=cache,target=/app/node_modules,id=my_app_npm_module,sharing=locked \
--mount=type=cache,target=/root/.npm,id=npm_cache \
npm i --registry=https://registry.npmmirror.com
COPY src /app/src
RUN --mount=type=cache,target=/app/node_modules,id=my_app_npm_module,sharing=locked \
# --mount=type=cache,target=/app/dist,id=my_app_dist,sharing=locked \
npm run build
FROM nginx:alpine
# COPY --from=builder /app/dist /app/dist
# 为了更直观的说明 from 和 source 指令,这里使用 RUN 指令
RUN --mount=type=cache,target=/tmp/dist,from=builder,source=/app/dist \
# --mount=type=cache,target/tmp/dist,from=my_app_dist,sharing=locked \
mkdir -p /app/dist && cp -r /tmp/dist/* /app/dist
RUN --mount=type=bind,from=php:alpine,source=/usr/local/bin/docker-php-entrypoint,target=/docker-php-entrypoint \
cat /docker-php-entrypoint
RUN --mount=type=tmpfs,target=/temp \
mount | grep /temp
RUN --mount=type=secret,id=aws,target=/root/.aws/credentials \
cat /root/.aws/credentials
# docker build -t test --secret id=aws,src=$PWD/aws.txt --progress=plain -f Dockerfile.buildkit .
================================================
FILE: 04_image/demo/buildkit/aws.txt
================================================
awskey
================================================
FILE: 04_image/demo/buildkit/package.json
================================================
{
"name": "my_app",
"version": "19.6.0",
"devDependencies": {
"webpack": "*",
"webpack-cli": "*"
},
"scripts": {
"build": "mkdir -p $PWD/dist && cp -r src/* dist/"
}
}
================================================
FILE: 04_image/demo/buildkit/src/index.js
================================================
console.log(1);
================================================
FILE: 04_image/demo/multi-arch/Dockerfile
================================================
FROM --platform=$TARGETPLATFORM alpine
RUN uname -a > /os.txt
CMD cat /os.txt
================================================
FILE: 04_image/demo/multistage-builds/.gitignore
================================================
app
================================================
FILE: 04_image/demo/multistage-builds/Dockerfile
================================================
FROM golang:alpine as builder
RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld/
RUN go get -d -v github.com/go-sql-driver/mysql
COPY app.go .
RUN CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
FROM alpine:latest as prod
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY --from=0 /go/src/github.com/go/helloworld/app .
CMD ["./app"]
================================================
FILE: 04_image/demo/multistage-builds/Dockerfile.build
================================================
FROM golang:alpine
RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld
COPY app.go .
RUN go get -d -v github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
================================================
FILE: 04_image/demo/multistage-builds/Dockerfile.copy
================================================
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]
================================================
FILE: 04_image/demo/multistage-builds/Dockerfile.one
================================================
FROM golang:alpine
RUN apk --no-cache add git ca-certificates
WORKDIR /go/src/github.com/go/helloworld/
COPY app.go .
RUN go get -d -v github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . \
&& cp /go/src/github.com/go/helloworld/app /root
WORKDIR /root/
CMD ["./app"]
================================================
FILE: 04_image/demo/multistage-builds/app.go
================================================
package main
import "fmt"
func main(){
fmt.Printf("Hello World!");
}
================================================
FILE: 04_image/demo/multistage-builds/build.sh
================================================
#!/bin/sh
echo Building go/helloworld:build
docker build -t go/helloworld:build . -f Dockerfile.build
docker create --name extract go/helloworld:build
docker cp extract:/go/src/github.com/go/helloworld/app ./app
docker rm -f extract
echo Building go/helloworld:2
docker build --no-cache -t go/helloworld:2 . -f Dockerfile.copy
rm ./app
================================================
FILE: 04_image/summary.md
================================================
## 本章小结
本章介绍了 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)。
================================================
FILE: 05_container/5.1_run.md
================================================
## 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)。
================================================
FILE: 05_container/5.2_daemon.md
================================================
## 5.2 守护态运行
在生产环境中,我们通常需要容器持续运行,不受终端关闭的影响。本节将深入讲解如何让容器在后台运行,以及理解容器生命周期的核心概念。
### 5.2.1 核心概念:前台 vs 后台
当你在终端运行一个程序时,有两种模式:
- **前台运行**:程序占用当前终端,输出直接显示,关闭终端程序就停止
- **后台运行**:程序在后台执行,不占用终端,终端关闭也不影响程序
Docker 容器默认是 **前台运行** 的。使用 `-d` (detach) 参数可以让容器在后台运行。
### 5.2.2 基本使用
#### 前台运行:默认
```bash
$ docker run ubuntu:24.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
hello world
hello world
hello world
hello world
```
容器会把输出的结果 (STDOUT) 打印到宿主机上面。此时:
- 终端被占用,无法执行其他命令
- 按 `Ctrl+C` 会终止容器
- 关闭终端窗口,容器也会停止
#### 后台运行:使用 -d 参数
```bash
$ docker run -d ubuntu:24.04 /bin/sh -c "while true; do echo hello world; sleep 1; done"
77b2dc01fe0f3f1265df143181e7b9af5e05279a884f4776ee75350ea9d8017a
```
使用 `-d` 参数后:
- 容器在后台运行
- 返回容器的完整 ID
- 终端立即释放,可以继续执行其他命令
- 输出不会直接显示 (需要用 `docker logs` 查看)
### 5.2.3 深入理解:容器为什么会 “立即退出”?
> **这是初学者最常遇到的困惑。** 理解这个问题,你就理解了 Docker 的核心设计理念。
很多人尝试这样启动容器:
```bash
$ docker run -d ubuntu:24.04
```
然后用 `docker ps` 查看,发现容器根本不在运行!这是为什么?
#### 核心原理:容器的生命周期与主进程绑定
```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 发现没有输入源
4. bash 立即退出
5. 主进程退出,容器停止
**关键理解**:
- ❌ `-d` 参数 **不是** 让容器 “一直运行”
- ✅ `-d` 参数是让容器 “在后台运行”,能运行多久取决于主进程
#### 常见的 “立即退出” 场景
| 场景 | 原因 | 解决方案 |
|------|------|---------|
| `docker run -d ubuntu` | 默认 bash 无输入立即退出 | 指定长期运行的命令 |
| `docker run -d nginx` 后改了配置 | 配置错误导致 nginx 启动失败 | 查看 `docker logs` |
| 自定义镜像容器启动即退 | Dockerfile 的 CMD 执行完毕 | 确保 CMD 是前台进程 |
### 5.2.4 查看后台容器
#### 查看运行中的容器
```bash
$ docker container ls
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
77b2dc01fe0f ubuntu:24.04 /bin/sh -c 'while tr 2 minutes ago Up 1 minute agitated_wright
```
#### 查看容器输出日志
```bash
$ docker container logs 77b2dc01fe0f
hello world
hello world
hello world
...
```
**实时查看日志** (类似 `tail -f`):
```bash
$ docker container logs -f 77b2dc01fe0f
```
#### 查看已停止的容器
```bash
$ docker container ls -a
```
加上 `-a` 参数可以看到所有容器,包括已停止的。这对于调试 “容器启动即退出” 的问题非常有用。
### 5.2.5 最佳实践
#### 1. 长期运行的服务使用 -d
```bash
## Web 服务器
$ docker run -d -p 80:80 nginx
## 数据库
$ docker run -d -p 3306:3306 mysql:8
## 缓存服务
$ docker run -d -p 6379:6379 redis
```
#### 2. 调试时先用前台模式
当容器启动有问题时,**去掉 `-d` 参数** 可以直接看到输出和错误:
```bash
## 有问题的容器,先前台运行看看发生了什么
$ docker run myimage:latest
```
#### 3. 使用 --rm 自动清理
对于一次性任务,使用 `--rm` 参数让容器退出后自动删除:
```bash
$ docker run --rm ubuntu:24.04 echo "Hello, World!"
Hello, World!
## 容器执行完后自动删除
...
```
#### 4. 配合日志查看
```bash
## 后台启动
$ docker run -d --name myapp myimage:latest
## 查看最近 100 行日志
$ docker logs --tail 100 myapp
## 实时跟踪日志
$ docker logs -f myapp
## 查看带时间戳的日志
$ docker logs -t myapp
```
### 5.2.6 常见问题排查
#### Q:容器启动后立即退出
1. **查看退出状态码**:
```bash
$ docker ps -a --filter "name=mycontainer"
# 查看 STATUS 列,如 “Exited (1)” 表示异常退出
```
2. **查看容器日志**:
```bash
$ docker logs mycontainer
```
3. **以交互模式调试**:
```bash
$ docker run -it myimage:latest /bin/sh
# 进入容器手动执行命令,查找问题
```
#### Q:容器在后台运行但无法访问服务
1. **检查端口映射**:
```bash
$ docker port mycontainer
```
2. **检查容器内服务状态**:
```bash
$ docker exec mycontainer ps aux
```
#### Q:如何让已经在后台运行的容器回到前台?
使用 `docker attach`:
```bash
$ docker attach mycontainer
```
> **注意**:`attach` 会连接到容器的主进程。如果主进程不是交互式的,你可能只能看到输出。使用 `Ctrl+P` `Ctrl+Q` 可以安全退出而不停止容器。
### 5.2.7 延伸阅读
- [进入容器](5.4_attach_exec.md):如何进入正在运行的容器执行命令
- [容器日志](../appendix/best_practices.md):生产环境的日志管理最佳实践
- [HEALTHCHECK 健康检查](../07_dockerfile/7.12_healthcheck.md):自动检测容器内服务是否正常
- [Docker Compose](../11_compose/README.md):管理多个后台容器的更好方式
================================================
FILE: 05_container/5.3_stop.md
================================================
## 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 容器名
```
---
================================================
FILE: 05_container/5.4_attach_exec.md
================================================
## 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 debug myapp
```
---
### 5.4.7 常见问题
#### Q:exec 进入后看不到其他终端的操作
这是正常的。exec 启动的是独立进程,多个 exec 会话互不影响。
#### Q:容器没有 bash
尝试使用 sh:
```bash
$ docker exec -it myapp /bin/sh
```
#### Q:需要 root 权限
```bash
$ docker exec -u root -it myapp bash
```
---
================================================
FILE: 05_container/5.5_import_export.md
================================================
## 5.5 导出和导入
当我们需要迁移容器或者备份容器时,可以使用 Docker 的导入和导出功能。本节将介绍这两个命令的使用方法。
### 5.5.1 导出容器
如果要导出本地某个容器,可以使用 `docker export` 命令。
```bash
$ docker container ls -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
7691a814370e ubuntu:24.04 "/bin/bash" 36 hours ago Exited (0) 21 hours ago test
$ docker export 7691a814370e > ubuntu.tar
```
这样将导出容器快照到本地文件。
### 5.5.2 导入容器快照
可以使用 `docker import` 从容器快照文件中再导入为镜像,例如
```bash
$ cat ubuntu.tar | docker import - test/ubuntu:v1.0
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
test/ubuntu v1.0 9d37a6082e97 About a minute ago 171.3 MB
```
此外,也可以通过指定 URL 或者某个目录来导入,例如
```bash
$ docker import http://example.com/exampleimage.tgz example/imagerepo
```
*注:用户既可以使用 `docker load` 来导入镜像存储文件到本地镜像库,也可以使用 `docker import` 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息 (即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。*
================================================
FILE: 05_container/5.6_rm.md
================================================
## 5.6 删除
随着容器的创建和停止,系统中会积累大量的容器。本节将介绍如何删除不再需要的容器,以及如何清理所有停止的容器。
### 5.6.1 基本用法
使用 `docker rm` 删除已停止的容器:
```bash
$ docker rm 容器名或ID
```
> 💡 `docker rm` 是 `docker container rm` 的简写,两者等效。
---
### 5.6.2 删除选项
| 选项 | 说明 | 示例 |
|------|------|------|
| 无参数 | 删除已停止的容器 | `docker rm mycontainer` |
| `-f` | 强制删除运行中的容器 | `docker rm -f mycontainer` |
| `-v` | 同时删除关联的匿名卷 | `docker rm -v mycontainer` |
#### 删除已停止的容器
```bash
$ docker rm mycontainer
mycontainer
```
#### 强制删除运行中的容器
```bash
## 不加 -f 会报错
$ docker rm running_container
Error: cannot remove running container
## 加 -f 强制删除
$ docker rm -f running_container
running_container
```
> ⚠️ 强制删除会向容器发送 SIGKILL 信号,可能导致数据丢失。建议先 `docker stop` 优雅停止。
#### 删除容器及其数据卷
```bash
## 删除容器时同时删除其匿名卷
$ docker rm -v mycontainer
```
> 注意:只删除匿名卷,命名卷不会被删除。
---
### 5.6.3 批量删除
#### 删除所有已停止的容器
```bash
## 方式一:使用 prune 命令(推荐)
$ docker container prune
WARNING! This will remove all stopped containers.
Are you sure you want to continue? [y/N] y
Deleted Containers:
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" 的容器
$ docker rm $(docker ps -aq -f name=test)
## 删除 24 小时前创建的容器
$ docker container prune --filter "until=24h"
```
---
### 5.6.4 常用过滤条件
`docker ps` 的过滤条件可以配合 `rm` 使用:
| 过滤条件 | 说明 | 示例 |
|---------|------|------|
| `status=exited` | 已退出的容器 | `-f status=exited` |
| `status=created` | 已创建未启动 | `-f status=created` |
| `name=xxx` | 名称匹配 | `-f name=myapp` |
| `ancestor=xxx` | 基于某镜像创建 | `-f ancestor=nginx` |
| `before=xxx` | 在某容器之前创建 | `-f before=mycontainer` |
| `since=xxx` | 在某容器之后创建 | `-f since=mycontainer` |
#### 示例
```bash
## 删除所有基于 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"
## 容器退出后自动删除
## 定期清理(设置保留时间)
$ docker container prune --filter "until=168h" # 保留 7 天内的
```
#### 完整清理脚本
```bash
#!/bin/bash
## cleanup.sh - Docker 资源清理脚本
echo "清理已停止的容器..."
docker container prune -f
echo "清理未使用的镜像..."
docker image prune -f
echo "清理未使用的数据卷..."
docker volume prune -f
echo "清理未使用的网络..."
docker network prune -f
echo "清理完成!"
docker system df
```
---
### 5.6.7 常见问题
#### Q:容器无法删除
```bash
Error: container is running
```
解决:先停止容器,或使用 `-f` 强制删除
```bash
$ docker stop mycontainer
$ docker rm mycontainer
## 或
$ docker rm -f mycontainer
```
#### Q:删除后磁盘空间没释放
可能原因:
1. 容器的数据卷未删除 (使用 `-v` 参数)
2. 镜像未删除
3. 构建缓存未清理
解决:
```bash
## 查看空间占用
$ docker system df
## 完整清理
$ docker system prune -a --volumes
```
---
================================================
FILE: 05_container/README.md
================================================
# 第五章 操作容器
容器是 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)
================================================
FILE: 05_container/summary.md
================================================
## 本章小结
本章介绍了 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)。
================================================
FILE: 06_repository/6.1_dockerhub.md
================================================
## 6.1 Docker Hub
### 6.1.1 什么是 Docker Hub
Docker Hub 是 Docker 的中央镜像仓库,通过它您可以轻松地分享和获取 Docker 镜像。
[Docker Hub](https://hub.docker.com/) 是 Docker 官方维护的公共镜像仓库,也是全球最大的容器镜像库。
它提供了:
- **官方镜像**:由 Docker 官方和软件厂商 (如 Nginx,MySQL,Node.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 会自动运行构建。这保证了镜像总是与代码同步,且由可信的官方环境构建。
---
================================================
FILE: 06_repository/6.2_registry.md
================================================
## 6.2 私有仓库
有时候使用 Docker Hub 这样的公共仓库可能不方便,用户可以创建一个本地仓库供私人使用。
本节介绍如何使用本地仓库。
[Docker Registry](https://docs.docker.com/registry/) 是官方提供的工具,可以用于构建私有的镜像仓库。本文内容基于 [docker/distribution](https://github.com/docker/distribution) v2.x 版本。
### 6.2.1 安装运行 docker-registry
#### 容器运行
如果您需要搭建私有仓库,可以通过官方提供的 `registry` 镜像快速部署。
你可以使用官方 `registry` 镜像来运行。
```bash
$ docker run -d -p 5000:5000 --restart=always --name registry registry
```
这将使用官方的 `registry` 镜像来启动私有仓库。默认情况下,仓库会被创建在容器的 `/var/lib/registry` 目录下。你可以通过 `-v` 参数来将镜像文件存放在本地的指定路径。例如下面的例子将上传的镜像放到本地的 `/opt/data/registry` 目录。
```bash
$ docker run -d \
-p 5000:5000 \
-v /opt/data/registry:/var/lib/registry \
registry
```
### 6.2.2 在私有仓库上传、搜索、下载镜像
创建好私有仓库之后,就可以使用 `docker tag` 来标记一个镜像,然后推送它到仓库。例如私有仓库地址为 `127.0.0.1:5000`。
先在本机查看已有的镜像。
```bash
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
ubuntu latest ba5877dc9bec 6 weeks ago 192.7 MB
```
使用 `docker tag` 将 `ubuntu:latest` 这个镜像标记为 `127.0.0.1:5000/ubuntu:latest`。
格式为 `docker tag IMAGE[:TAG] [REGISTRY_HOST[:REGISTRY_PORT]/]REPOSITORY[:TAG]`。
```bash
$ docker tag ubuntu:latest 127.0.0.1:5000/ubuntu:latest
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
ubuntu latest ba5877dc9bec 6 weeks ago 192.7 MB
127.0.0.1:5000/ubuntu:latest latest ba5877dc9bec 6 weeks ago 192.7 MB
```
使用 `docker push` 上传标记的镜像。
```bash
$ docker push 127.0.0.1:5000/ubuntu:latest
The push refers to repository [127.0.0.1:5000/ubuntu]
373a30c24545: Pushed
a9148f5200b0: Pushed
cdd3de0940ab: Pushed
fc56279bbb33: Pushed
b38367233d37: Pushed
2aebd096e0e2: Pushed
latest: digest: sha256:fe4277621f10b5026266932ddf760f5a756d2facd505a94d2da12f4f52f71f5a size: 1568
```
用 `curl` 查看仓库中的镜像。
```bash
$ curl 127.0.0.1:5000/v2/_catalog
{"repositories":["ubuntu"]}
```
这里可以看到 `{"repositories":["ubuntu"]}`,表明镜像已经被成功上传了。
先删除已有镜像,再尝试从私有仓库中下载这个镜像。
```bash
$ docker image rm 127.0.0.1:5000/ubuntu:latest
$ docker pull 127.0.0.1:5000/ubuntu:latest
Pulling repository 127.0.0.1:5000/ubuntu:latest
ba5877dc9bec: Download complete
511136ea3c5a: Download complete
9bad880da3d2: Download complete
25f11f5fb0cb: Download complete
ebc34468f71d: Download complete
2318d26665ef: Download complete
$ docker image ls
REPOSITORY TAG IMAGE ID CREATED VIRTUAL SIZE
127.0.0.1:5000/ubuntu:latest latest ba5877dc9bec 6 weeks ago 192.7 MB
```
### 6.2.3 配置非 https 仓库地址
如果你不想使用 `127.0.0.1:5000` 作为仓库地址,比如想让本网段的其他主机也能把镜像推送到私有仓库。你就得把例如 `192.168.199.100:5000` 这样的内网地址作为私有仓库地址,这时你会发现无法成功推送镜像。
这是因为 Docker 默认不允许非 `HTTPS` 方式推送镜像。我们可以通过 Docker 的配置选项来取消这个限制,或者查看下一节配置能够通过 `HTTPS` 访问的私有仓库。
#### Linux
默认情况下,Docker 强制使用 HTTPS 协议推送镜像。如果您搭建的私有仓库是 HTTP 协议,需要进行如下配置。
对于使用 `systemd` 的系统,请在 `/etc/docker/daemon.json` 中写入如下内容 (如果文件不存在请新建该文件)
```json
{
"registry-mirrors": [
"https://hub-mirror.c.163.com",
"https://mirror.baidubce.com"
],
"insecure-registries": [
"192.168.199.100:5000"
]
}
```
> 注意:该文件必须符合 `json` 规范,否则 Docker 将不能启动。
### 6.2.4 其他
对于 Docker Desktop for Windows、Docker Desktop for Mac 在设置中的 `Docker Engine` 中进行编辑,增加和上边一样的字符串即可。
================================================
FILE: 06_repository/6.3_registry_auth.md
================================================
## 6.3 私有仓库高级配置
上一节我们搭建了一个具有基础功能的私有仓库,本小节我们来使用 `Docker Compose` 搭建一个拥有权限认证、TLS 的私有仓库。
新建一个文件夹,以下步骤均在该文件夹中进行。
### 6.3.1 准备站点证书
如果你拥有一个域名,国内各大云服务商均提供免费的站点证书。你也可以使用 `openssl` 自行签发证书。
这里假设我们将要搭建的私有仓库地址为 `docker.domain.com`,下面我们介绍使用 `openssl` 自行签发 `docker.domain.com` 的站点 SSL 证书。
第一步创建 `CA` 私钥。
```bash
$ openssl genrsa -out "root-ca.key" 4096
```
第二步利用私钥创建 `CA` 根证书请求文件。
```bash
$ openssl req \
-new -key "root-ca.key" \
-out "root-ca.csr" -sha256 \
-subj '/C=CN/ST=Shanxi/L=Datong/O=Your Company Name/CN=Your Company Name Docker Registry CA'
```
> 以上命令中 `-subj` 参数里的 `/C` 表示国家,如 `CN`;`/ST` 表示省;`/L` 表示城市或者地区;`/O` 表示组织名;`/CN` 通用名称。
第三步配置 `CA` 根证书,新建 `root-ca.cnf`。
```bash
[root_ca]
basicConstraints = critical,CA:TRUE,pathlen:1
keyUsage = critical, nonRepudiation, cRLSign, keyCertSign
subjectKeyIdentifier=hash
```
第四步签发根证书。
```bash
$ openssl x509 -req -days 3650 -in "root-ca.csr" \
-signkey "root-ca.key" -sha256 -out "root-ca.crt" \
-extfile "root-ca.cnf" -extensions \
root_ca
```
第五步生成站点 `SSL` 私钥。
```bash
$ openssl genrsa -out "docker.domain.com.key" 4096
```
第六步使用私钥生成证书请求文件。
```bash
$ openssl req -new -key "docker.domain.com.key" -out "site.csr" -sha256 \
-subj '/C=CN/ST=Shanxi/L=Datong/O=Your Company Name/CN=docker.domain.com'
```
第七步配置证书,新建 `site.cnf` 文件。
```bash
[server]
authorityKeyIdentifier=keyid,issuer
basicConstraints = critical,CA:FALSE
extendedKeyUsage=serverAuth
keyUsage = critical, digitalSignature, keyEncipherment
subjectAltName = DNS:docker.domain.com, IP:127.0.0.1
subjectKeyIdentifier=hash
```
第八步签署站点 `SSL` 证书。
```bash
$ openssl x509 -req -days 750 -in "site.csr" -sha256 \
-CA "root-ca.crt" -CAkey "root-ca.key" -CAcreateserial \
-out "docker.domain.com.crt" -extfile "site.cnf" -extensions server
```
这样已经拥有了 `docker.domain.com` 的网站 SSL 私钥 `docker.domain.com.key` 和 SSL 证书 `docker.domain.com.crt` 及 CA 根证书 `root-ca.crt`。
新建 `ssl` 文件夹并将 `docker.domain.com.key` `docker.domain.com.crt` `root-ca.crt` 这三个文件移入,删除其他文件。
### 6.3.2 配置私有仓库
私有仓库默认的配置文件位于 `/etc/docker/registry/config.yml`,我们先在本地编辑 `config.yml`,之后挂载到容器中。
```yaml
log:
accesslog:
disabled: true
level: debug
formatter: text
fields:
service: registry
environment: staging
storage:
delete:
enabled: true
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
auth:
htpasswd:
realm: basic-realm
path: /etc/docker/registry/auth/nginx.htpasswd
http:
addr: :443
host: https://docker.domain.com
headers:
X-Content-Type-Options: [nosniff]
http2:
disabled: false
tls:
certificate: /etc/docker/registry/ssl/docker.domain.com.crt
key: /etc/docker/registry/ssl/docker.domain.com.key
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
```
### 6.3.3 生成 http 认证文件
```bash
$ mkdir auth
$ docker run --rm \
--entrypoint htpasswd \
httpd:alpine \
-Bbn username password > auth/nginx.htpasswd
```
> 将上面的 `username` `password` 替换为你自己的用户名和密码。
### 6.3.4 编辑 Docker Compose 配置
编辑 `compose.yaml` (或 `docker-compose.yml`) 配置如下:
```yaml
services:
registry:
image: registry
ports:
- "443:443"
volumes:
- ./:/etc/docker/registry
- registry-data:/var/lib/registry
volumes:
registry-data:
```
### 6.3.5 修改 Hosts 文件
编辑 `/etc/hosts`
```bash
127.0.0.1 docker.domain.com
```
### 6.3.6 启动
```bash
$ docker compose up -d
```
这样我们就搭建好了一个具有权限认证、TLS 的私有仓库,接下来我们测试其功能是否正常。
### 6.3.7 测试私有仓库功能
由于自行签发的 CA 根证书不被系统信任,所以我们需要将 CA 根证书 `ssl/root-ca.crt` 移入 `/etc/docker/certs.d/docker.domain.com` 文件夹中。
```bash
$ sudo mkdir -p /etc/docker/certs.d/docker.domain.com
$ sudo cp ssl/root-ca.crt /etc/docker/certs.d/docker.domain.com/ca.crt
```
登录到私有仓库。
```bash
$ docker login docker.domain.com
```
尝试推送、拉取镜像。
```bash
$ docker pull ubuntu:24.04
$ docker tag ubuntu:24.04 docker.domain.com/username/ubuntu:24.04
$ docker push docker.domain.com/username/ubuntu:24.04
$ docker image rm docker.domain.com/username/ubuntu:24.04
$ docker pull docker.domain.com/username/ubuntu:24.04
```
如果我们退出登录,尝试推送镜像。
```bash
$ docker logout docker.domain.com
$ docker push docker.domain.com/username/ubuntu:24.04
no basic auth credentials
```
发现会提示没有登录,不能将镜像推送到私有仓库中。
### 6.3.8 注意事项
如果你本机占用了 `443` 端口,你可以配置 [Nginx 代理](https://docs.docker.com/registry/recipes/nginx/),这里不再赘述。
================================================
FILE: 06_repository/6.4_nexus3_registry.md
================================================
## 6.4 Nexus 3
使用 Docker 官方的 Registry 创建的仓库面临一些维护问题。比如某些镜像删除以后空间默认是不会回收的,需要一些命令去回收空间然后重启 Registry。在企业中把内部的一些工具包放入 `Nexus` 中是比较常见的做法,最新版本 `Nexus3.x` 全面支持 Docker 的私有镜像。所以使用 [Nexus 3](https://www.sonatype.com/product/repository-oss-download) 一个软件来管理 `Docker`,`Maven`,`Yum`,`PyPI` 等是一个明智的选择。
### 6.4.1 启动 Nexus 容器
```bash
$ docker run -d --name nexus3 --restart=always \
-p 8081:8081 \
--mount src=nexus-data,target=/nexus-data \
sonatype/nexus3
```
首次运行需等待 3-5 分钟,你可以使用 `docker logs nexus3 -f` 查看日志:
```bash
$ docker logs nexus3 -f
2021-03-11 15:31:21,990+0000 INFO [jetty-main-1] *SYSTEM org.sonatype.nexus.bootstrap.jetty.JettyServer -
-------------------------------------------------
Started Sonatype Nexus OSS 3.30.0-01
-------------------------------------------------
```
如果你看到以上内容,说明 `Nexus` 已经启动成功,你可以使用浏览器打开 `http://YourIP:8081` 访问 `Nexus` 了。
首次运行请通过以下命令获取初始密码:
```bash
$ docker exec nexus3 cat /nexus-data/admin.password
9266139e-41a2-4abb-92ec-e4142a3532cb
```
首次启动 Nexus 的默认账号是 `admin`,密码则是上边命令获取到的,点击右上角登录,首次登录需更改初始密码。
登录之后可以点击页面上方的齿轮按钮按照下面的方法进行设置。
### 6.4.2 创建仓库
创建一个私有仓库的方法:`Repository->Repositories` 点击右边菜单 `Create repository` 选择 `docker (hosted)`
* **Name**:仓库的名称
* **HTTP**:仓库单独的访问端口 (例如:**5001**)
* **Hosted -> Deployment policy**:请选择 **Allow redeploy** 否则无法上传 Docker 镜像。
其它的仓库创建方法请各位自己摸索,还可以创建一个 `docker (proxy)` 类型的仓库链接到 DockerHub 上。再创建一个 `docker (group)` 类型的仓库把刚才的 `hosted` 与 `proxy` 添加在一起。主机在访问的时候默认下载私有仓库中的镜像,如果没有将链接到 DockerHub 中下载并缓存到 Nexus 中。
### 6.4.3 添加访问权限
菜单 `Security->Realms` 把 Docker Bearer Token Realm 移到右边的框中保存。
添加用户规则:菜单 `Security->Roles`->`Create role` 在 `Privileges` 选项搜索 docker 把相应的规则移动到右边的框中然后保存。
添加用户:菜单 `Security->Users`->`Create local user` 在 `Roles` 选项中选中刚才创建的规则移动到右边的窗口保存。
### 6.4.4 NGINX 反向代理配置
证书的生成请参见 [私有仓库认证](6.3_registry_auth.md) 里面证书生成一节。
NGINX 示例配置如下
```nginx
upstream register
{
server "YourHostName OR IP":5001; #端口为上面添加私有镜像仓库时设置的 HTTP 选项的端口号
check interval=3000 rise=2 fall=10 timeout=1000 type=http;
check_http_send "HEAD / HTTP/1.0\r\n\r\n";
check_http_expect_alive http_4xx;
}
server {
server_name YourDomainName;#如果没有 DNS 服务器做解析,请删除此选项使用本机 IP 地址访问
listen 443 ssl;
ssl_certificate key/example.crt;
ssl_certificate_key key/example.key;
ssl_session_timeout 5m;
ssl_protocols TLSv1 TLSv1.1 TLSv1.2;
ssl_ciphers HIGH:!aNULL:!MD5;
ssl_prefer_server_ciphers on;
large_client_header_buffers 4 32k;
client_max_body_size 300m;
client_body_buffer_size 512k;
proxy_connect_timeout 600;
proxy_read_timeout 600;
proxy_send_timeout 600;
proxy_buffer_size 128k;
proxy_buffers 4 64k;
proxy_busy_buffers_size 128k;
proxy_temp_file_write_size 512k;
location / {
proxy_set_header Host $host;
proxy_set_header X-Forwarded-Proto $scheme;
proxy_set_header X-Forwarded-Port $server_port;
proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Connection $connection_upgrade;
proxy_redirect off;
proxy_set_header X-Real-IP $remote_addr;
proxy_pass http://register;
proxy_read_timeout 900s;
}
error_page 500 502 503 504 /50x.html;
}
```
### 6.4.5 Docker 主机访问镜像仓库
如果不启用 SSL 加密可以通过[前面章节](6.2_registry.md)的方法添加非 https 仓库地址到 Docker 的配置文件中然后重启 Docker。
使用 SSL 加密以后程序需要访问就不能采用修改配置的方式了。具体方法如下:
```bash
$ openssl s_client -showcerts -connect YourDomainName OR HostIP:443 </dev/null 2>/dev/null|openssl x509 -outform PEM >ca.crt
$ cat ca.crt | sudo tee -a /etc/ssl/certs/ca-certificates.crt
$ systemctl restart docker
```
使用 `docker login YourDomainName OR HostIP` 进行测试,用户名密码填写上面 Nexus 中设置的。
================================================
FILE: 06_repository/README.md
================================================
# 第六章 访问仓库
仓库 (`Repository`) 是集中存放镜像的地方。
一个容易混淆的概念是注册服务器 (`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)
================================================
FILE: 06_repository/demo/auth/nginx.htpasswd
================================================
username:$2y$05$TRWvCC6ilpKpY3ICifw32Ok3.8SpG3etq8O5WGdCm9wvyDhtSbRgy
================================================
FILE: 06_repository/demo/config.yml
================================================
version: 0.1
log:
accesslog:
disabled: true
level: debug
formatter: text
fields:
service: registry
environment: staging
storage:
delete:
enabled: true
cache:
blobdescriptor: inmemory
filesystem:
rootdirectory: /var/lib/registry
auth:
htpasswd:
realm: basic-realm
path: /etc/docker/registry/auth/nginx.htpasswd
http:
addr: :5000
host: https://docker.domain.com
headers:
X-Content-Type-Options: [nosniff]
http2:
disabled: false
tls:
certificate: /etc/docker/registry/ssl/docker.domain.com.crt
key: /etc/docker/registry/ssl/docker.domain.com.key
health:
storagedriver:
enabled: true
interval: 10s
threshold: 3
================================================
FILE: 06_repository/demo/docker-compose.yml
================================================
services:
registry:
image: registry:2
ports:
- "443:5000"
volumes:
- ./:/etc/docker/registry
- registry-data:/var/lib/registry
volumes:
registry-data:
================================================
FILE: 06_repository/demo/root-ca.cnf
================================================
[root_ca]
basicConstraints = critical,CA:TRUE,pathlen:1
keyUsage = critical, nonRepudiation, cRLSign, keyCertSign
subjectKeyIdentifier=hash
================================================
FILE: 06_repository/demo/site.cnf
================================================
[server]
authorityKeyIdentifier=keyid,issuer
basicConstraints = critical,CA:FALSE
extendedKeyUsage=serverAuth
keyUsage = critical, digitalSignature, keyEncipherment
subjectAltName = DNS:docker.domain.com, IP:127.0.0.1
subjectKeyIdentifier=hash
================================================
FILE: 06_repository/demo/ssl/docker.domain.com.crt
================================================
-----BEGIN CERTIFICATE-----
MIIF/zCCA+egAwIBAgIJAMbgVbFo7I6IMA0GCSqGSIb3DQEBCwUAMHoxCzAJBgNV
BAYTAkNOMQ8wDQYDVQQIDAZTaGFueGkxDzANBgNVBAcMBkRhdG9uZzEaMBgGA1UE
CgwRWW91ciBDb21wYW55IE5hbWUxLTArBgNVBAMMJFlvdXIgQ29tcGFueSBOYW1l
IERvY2tlciBSZWdpc3RyeSBDQTAeFw0xNzEyMTExMTI2NTRaFw0xOTEyMzExMTI2
NTRaMGcxCzAJBgNVBAYTAkNOMQ8wDQYDVQQIDAZTaGFueGkxDzANBgNVBAcMBkRh
dG9uZzEaMBgGA1UECgwRWW91ciBDb21wYW55IE5hbWUxGjAYBgNVBAMMEWRvY2tl
ci5kb21haW4uY29tMIICIjANBgkqhkiG9w0BAQEFAAOCAg8AMIICCgKCAgEA1Hbm
1tZvAeC8J54pQLHTVCtrICQ8KFTLOZakPHSWox8iQ4i2fkZaSccvE/51LIFnCM1y
yZv88ILucqitG4zvuhDG+cU8w1vRbWf3xfGCCsHyn6LKBHR0Kk6+WRIBZTdRSsW8
ZvpL2Y7eBNAUkC2oeaOJEOQ8D50b3u5jhAFmXuAcTiZh90Ve4JBKZV8dGs8L81vO
vb7tqvJrEvCNKuZO7mEcjXkgiwUdP3pkZYa0tPOV5UrLH/oEvgPDJfXrNntCY1+A
+CBQ7Sq3S2YpNJN7VnK6SboRi7xpOEgQOXwNVJWm/5YBvnbAztNXKcE2q2wREL9T
ulUJCqo2h6NRaGYPfiLZIUHEJ7vQitRBRDpkxcX8XJX3Jm7weRPXLDOG8RN3FzPD
TGL2ZDTWKDIQ20OhYEmENInKkpC2bYpwMzNvBA7AJN6MCYHXl4VP/Df0pPtMM/Di
ZX+68ng3RQXT5VzDYURIH2wP5kcTL0irsNr+L7lHD+5mA1VErdQsh6AHWjrwMrSM
egJpLP11rK4J+P/HHnqsBEi5XZI/hvRmwD7gwskRqXdDMHaWnoIIpPO3TeNYQUjD
xaV5KUQuWVME5Ihuy03FPMChXXB0WF+tclPJxjQyDkwEVL+6d8i5yCPUBS2gBSBy
pQQINFH4hOcx2e9Lujx7dOWfidjX3xssC9bEe60CAwEAAaOBmjCBlzAfBgNVHSME
GDAWgBTS1B3CPFKA/HcI0cdE8YD4zG143DAMBgNVHRMBAf8EAjAAMBMGA1UdJQQM
MAoGCCsGAQUFBwMBMA4GA1UdDwEB/wQEAwIFoDAiBgNVHREEGzAZghFkb2NrZXIu
ZG9tYWluLmNvbYcEfwAAATAdBgNVHQ4EFgQUSanj6Cs4KVEKaK6/+VA/fNwNg4Ew
DQYJKoZIhvcNAQELBQADggIBAKYtI1WKAL4FoSgH6sTZakw6h90uebrxm9ojeZTA
k0ues8bGTu3w3dsphd9J0V27oz/dGjkwoIzy4QMYC4h6epKVadWfDhnHUPUT1JIC
nGl7qFR539CSPzW+J1mVAGTZ1QONVxe6rFEDRXTsm9oUNq9LUB6a9EBO/9O0x2o7
SZVUJd2WfMGAhqYjKCtMt+8kQgPxayok5IwWLBf03nluoF09Xu1WbY3f9wGNrzQp
ulNlLzkU3f7+dVgF4lvIbr4MPWSQL2A0RYYjqWuwvUlXggtR+Nl6ldotDe7Ae18V
KhQPJzM4muHjRY5dLkzQIIAQifxNprZCYiurUCAmOyOcHYMt5RuiVUPlB/2hoP/E
tuFqq66v0qsE4mCfmJrRq+Yjfgcqsg1quRpjWh9DWOGa9HUeYFkLEKOgXybxVHJQ
ktYba34ZFfBJUMcbZRYrRH6R4zu4LpRiyiXm29F5ml9tarThDZB5g5DJ6BTEt3Zw
+qQHsIAcmHZvJPKEZmM6883gxbGQQ1Xt7iDrp94YRXMguBMbJwEsqI7w+25BHija
Hp4gctdoBvkQBYpXoEsn8wnguofqJt/JhVgu0EQXR4j3U0uI+Oo9ODHFb5t2T26w
EifwcLH+NyUNmUQH45lxaCzb+tqFlP7cbHsdPniaS4AtmBYwKNJMjcrgxnZPtfy1
7zJA
-----END CERTIFICATE-----
================================================
FILE: 06_repository/demo/ssl/docker.domain.com.key
================================================
-----BEGIN RSA PRIVATE KEY-----
MIIJKgIBAAKCAgEA1Hbm1tZvAeC8J54pQLHTVCtrICQ8KFTLOZakPHSWox8iQ4i2
fkZaSccvE/51LIFnCM1yyZv88ILucqitG4zvuhDG+cU8w1vRbWf3xfGCCsHyn6LK
BHR0Kk6+WRIBZTdRSsW8ZvpL2Y7eBNAUkC2oeaOJEOQ8D50b3u5jhAFmXuAcTiZh
90Ve4JBKZV8dGs8L81vOvb7tqvJrEvCNKuZO7mEcjXkgiwUdP3pkZYa0tPOV5UrL
H/oEvgPDJfXrNntCY1+A+CBQ7Sq3S2YpNJN7VnK6SboRi7xpOEgQOXwNVJWm/5YB
vnbAztNXKcE2q2wREL9TulUJCqo2h6NRaGYPfiLZIUHEJ7vQitRBRDpkxcX8XJX3
Jm7weRPXLDOG8RN3FzPDTGL2ZDTWKDIQ20OhYEmENInKkpC2bYpwMzNvBA7AJN6M
CYHXl4VP/Df0pPtMM/DiZX+68ng3RQXT5VzDYURIH2wP5kcTL0irsNr+L7lHD+5m
A1VErdQsh6AHWjrwMrSMegJpLP11rK4J+P/HHnqsBEi5XZI/hvRmwD7gwskRqXdD
MHaWnoIIpPO3TeNYQUjDxaV5KUQuWVME5Ihuy03FPMChXXB0WF+tclPJxjQyDkwE
VL+6d8i5yCPUBS2gBSBypQQINFH4hOcx2e9Lujx7dOWfidjX3xssC9bEe60CAwEA
AQKCAgEAqT/6zePOVFGhsXG17Rp7fY6E7PrQjVRW/A470QkTQui3U9MhhWAn5qPs
peHLl+ORn5qCOYawrSuwJdim5c6U3cUlrKzppbqMD7qFz8J+1HECBRcaFQhrzZQi
4DOOtwGlGYqBdgsnxyyfQng8GUq17ghPVQxrqAiAvktrLSosUaH4Cm1bFy7E0OFA
0pY9SjDrlTZqcA8bp1Ur5M+JtUX4VL85jp2SRgyR6xJlzdbMN2Xf3+OAAn4ZrwCy
QZgwgpsYHK9kvsSHkxa3IzJD2uUtmIUWT0sRVR6HN1V4z0I6IEqC2RG3W/Gf0GLd
CZ8oHNCem5e+bC33YO6NN+nrHN5Isb+itdbtE392P6FgPbM1um1zuuTTewaUyXS7
ATomznTpYXkHvCdvU5yOEH+yDYfcm99v77qVr0+arecVx2h0M2tqROwEza3Rw5Wp
928vyxPFde9HFHQG4SWRzCGfKpnvIT/ce2ayWHEDEbvzlwL9lokZqe0YYu5KDYTL
j3DsnzMPiMn6bpQUIBXlO5+eAh94vatPCriNpEkHw3aYQNyux1BmyvKxflj6pg9u
lxKWGng8YOW88ysXvXlAssjpDe+k/Cvaja3ZeV3pyRbMnK3ARHXi3mQ6wBsOVa6e
zt3cpBgXik7m7u6a82FhLafP2UfFIpW6WRTA28ercpKtHRKJJgUCggEBAPWJsfPa
4movMI6ofySMFm74u779aO5rGMQumR52vwlWyNDmOWNRrQaU/tpyaFOiPeIOGt2V
UM02rdWHbnAhsgYP0cjUs34aJV/Z4nunw5Jc6rrT+dhd3+ZgqM1X/sLt5JfEavHe
bV/cDV+xDp4FAXrLOPeRldvLPT1dmdQnirPGK3A1WWi5GQ+//Kas+lHuqECWYRrU
LVFx0tR/pmdCC5Lb58kuPOxFP4OaeC3PbGyA2y0gv+QR/5Nc0o1y/X6p6XIM5QxY
fg4gDKwSewrZ40+9taRNtgMQz3xKkeYmaNgLKnCBnLhDYdLZPAeYkOUIytzJbaYg
oWHzmdd5FIKCgzcCggEBAN2Ecd7lJRIISuQop5GQ44mRrsDZTxBWnZ9pn3VFWNfA
tF5MHofrtED7mXBSAt88TryqndcyC6qMvaS4Ifk1cNSFdz9FLsNzxJj4wp5Cj+e1
aSY5ARvXTXqTfKZErQNXFk1oCa+ARZIs5SPjh0LH+Iq2pd72cEap7pi3JT5RbOtE
ReDCAKayyejFMKtekzccishIwH/nzYlNaNBkmG7MZG8V+FFlxYSieIa7Ohcbmmj3
D3ssbi+y+peRt14wZB/daScybcIqdu544ZIvJOpm0i9rHxE49bodmTDhAZ9awqls
nRsyI0NNDExPDhCrzPogcRLVHn2KbcZbjGPS5jossjsCggEAazYchaXlhwfj4+ae
3Y5tnTbug46S6sfIoKDYKv0enS1PsidUl5FqQ517Slb6RspoyvPttyMjjPd7H+lq
x3tvCEaQC2kUltNDzn6M7gFq29XGiJ1WUqtqwGUkT8VEcEj/r2UMbV/50gl7rXTa
NRVqd/uUfEUNclNkAg+Ew6YgYi79eJlS2O85ii8CWqTdCDl1Lf57mANdZlqU/ERg
nGWyOAXdR3LxFxmFiilAoIAZj6cUDLhoEWXqeqXlKe4z0cLPNAV9Xc6l+/Tyk4/e
Ofa50m+7iGqGNwB4GIVW/292CB+YAFgX3j1N0YsZMxfi7J7SNWWegxNsZCDB49vy
oKnsMQKCAQEA0iurbnOyrF050SfRdQcnG4shZs/HeBT2EB3CsR1OocWwXBeUkBlO
OKl+d1cYan1ppw+qGlbdQr+t3u7lLPFLUBghf+I/8CmSyiCbZlR4/LrePOmw551r
YXU1uvtFu/mQq3ieV+k4GOyHq3lhCDd61QFedyESfbkVK8f4ihvvX3izZAAtZfwU
HcmZ174vpwZploWQPsrL9A2B+Na42ccLM2qA45nPwXv1Jr/U6b/CzPw7r/4DvTXv
FIeolrELDkCgWBQ8lxB7Lt96BZy9Rbiwi1TzcP++BQu4IOwbAfq23tCybu8vDde4
Z15KVf7qyBansdqKx0njxWNu2/dpgKCPqQKCAQEAzWV4aajox7eeTi3iudP1Oxu3
OBiu8xie4xq0mlM13tMxAQry/uOAuTxbaQ48mNsSXdeYeSgmT45lUvFWROqdVScK
8gh04G1NiRAkITzXwCCwKkAQxvQppgypZ+aksBHkFQFBAIg2/mLizS4cicXNEY4G
vb+RImfn8MSqSMLu3cJ8zgFyqfRg6F0oHg9EgEwvbnCLYglN6Xm5KlZutjW4eu7m
Q+1y0lg05e7vFyj2UIVylfzT/aoF/xzXzNt9/LZs0klO3vMmaVyTjuF/71+AyphH
FTmpNs1wpZU2IRqKOMrimIhk6TTxvMSaN4pxKdLPqgHYLWhwkbsdmqtTnCljRg==
-----END RSA PRIVATE KEY-----
================================================
FILE: 06_repository/summary.md
================================================
## 本章小结
本章介绍了 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)。
================================================
FILE: 07_dockerfile/7.10_workdir.md
================================================
## 7.10 WORKDIR 指定工作目录
### 7.10.1 基本语法
```docker
WORKDIR <工作目录路径>
```
`WORKDIR` 指定后续指令的工作目录。如果目录不存在,Docker 会自动创建。
---
### 7.10.2 基本用法
```docker
WORKDIR /app
RUN pwd # 输出 /app
RUN echo "hello" > world.txt # 创建 /app/world.txt
COPY . . # 复制到 /app/
```
---
### 7.10.3 为什么需要 WORKDIR
#### 常见错误
```docker
## ❌ 错误:cd 在下一个 RUN 中无效
RUN cd /app
RUN echo "hello" > world.txt # 文件在根目录!
```
#### 原因分析
```dockerfile
RUN cd /app
↓
启动容器 → cd /app(仅内存变化)→ 提交镜像层 → 容器销毁
│
↓ 工作目录未改变!
RUN echo "hello" > world.txt
↓
启动新容器(工作目录在 /)→ 创建 /world.txt
```
每个 RUN 都在新容器中执行,**前一个 RUN 的内存状态 (包括工作目录) 不会保留**。
#### 正确做法
```docker
## ✅ 正确:使用 WORKDIR
WORKDIR /app
RUN echo "hello" > world.txt # 创建 /app/world.txt
```
---
### 7.10.4 相对路径
WORKDIR 支持相对路径,基于上一个 WORKDIR:
```docker
WORKDIR /a
WORKDIR b
WORKDIR c
RUN pwd # 输出 /a/b/c
```
---
### 7.10.5 使用环境变量
```docker
ENV APP_HOME=/app
WORKDIR $APP_HOME
RUN pwd # 输出 /app
```
---
### 7.10.6 多阶段构建中的 WORKDIR
```docker
## 构建阶段
FROM node:20 AS builder
WORKDIR /build
COPY package*.json ./
RUN npm install
COPY . .
RUN npm run build
## 生产阶段
FROM nginx:alpine
WORKDIR /usr/share/nginx/html
COPY --from=builder /build/dist .
```
---
### 7.10.7 最佳实践
#### 1. 尽早设置 WORKDIR
```docker
FROM node:20
WORKDIR /app # 尽早设置
COPY package*.json ./
RUN npm install
COPY . .
CMD ["node", "server.js"]
```
#### 2. 使用绝对路径
```docker
## ✅ 推荐:绝对路径,意图明确
WORKDIR /app
## ⚠️ 避免:相对路径可能造成混淆
WORKDIR app
```
#### 3. 不要用 RUN cd
```docker
## ❌ 避免
RUN cd /app && echo "hello" > world.txt
## ✅ 推荐
WORKDIR /app
RUN echo "hello" > world.txt
```
#### 4. 适时重置 WORKDIR
```docker
WORKDIR /app
## ... 应用相关操作 ...
WORKDIR /data
## ... 数据相关操作 ...
...
```
---
### 7.10.8 与其他指令的关系
| 指令 | WORKDIR 的影响 |
|------|---------------|
| `RUN` | 在 WORKDIR 中执行命令 |
| `CMD` | 在 WORKDIR 中启动 |
| `ENTRYPOINT` | 在 WORKDIR 中启动 |
| `COPY` | 相对目标路径基于 WORKDIR |
| `ADD` | 相对目标路径基于 WORKDIR |
```docker
WORKDIR /app
RUN pwd # /app
COPY . . # 复制到 /app
CMD ["./start.sh"] # /app/start.sh
```
---
### 7.10.9 运行时覆盖
使用 `-w` 参数覆盖工作目录:
```bash
$ docker run -w /tmp myimage pwd
/tmp
```
---
================================================
FILE: 07_dockerfile/7.11_user.md
================================================
## 7.11 USER 指定当前用户
### 7.11.1 基本语法
```docker
USER <用户名>[:<用户组>]
USER <UID>[:<GID>]
```
`USER` 指令切换后续指令 (RUN、CMD、ENTRYPOINT) 的执行用户。
---
### 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 用户无法绑定 80、443 等端口。
**解决**:
1. 使用高端口 (如 8080)
2. 在运行时映射端口:`docker run -p 80:8080`
---
================================================
FILE: 07_dockerfile/7.12_healthcheck.md
================================================
## 7.12 HEALTHCHECK 健康检查
### 7.12.1 基本语法
```docker
HEALTHCHECK [选项] CMD <命令>
HEALTHCHECK NONE
```
`HEALTHCHECK` 指令告诉 Docker 如何判断容器状态是否正常。这是保障服务高可用的重要机制。
---
### 7.12.2 为什么需要 HEALTHCHECK
在没有 HEALTHCHECK 之前,Docker 只能通过 **进程退出码** 来判断容器状态。**问题场景**:
- Web 服务死锁,无法响应请求,但进程仍在运行
- 数据库正在启动中,尚未准备好接受连接
- 应用陷入死循环,CPU 爆满但进程存活
**引入 HEALTHCHECK 后**:
Docker 定期执行指定的检查命令,根据返回值判断容器是否 “健康”。
```bash
容器状态转换:
Starting ──成功──> Healthy ──失败N次──> Unhealthy
▲ │
└──────成功──────┘
```
---
### 7.12.3 基本用法
#### Web 服务检查
```docker
FROM nginx
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
HEALTHCHECK --interval=30s --timeout=3s --retries=3 \
CMD curl -fs http://localhost/ || exit 1
```
#### 命令返回值
- `0`:成功 (healthy)
- `1`:失败 (unhealthy)
- `2`:保留值 (不使用)
#### 常用选项
| 选项 | 说明 | 默认值 |
|------|------|--------|
| `--interval` | 两次检查的间隔 | 30s |
| `--timeout` | 检查命令的超时时间 | 30s |
| `--start-period` | 启动缓冲期 (期间失败不计入次数)| 0s |
| `--retries` | 连续失败多少次标记为 unhealthy | 3 |
---
### 7.12.4 屏蔽健康检查
如果基础镜像定义了 HEALTHCHECK,但你不想使用它:
```docker
FROM my-base-image
HEALTHCHECK NONE
```
---
### 7.12.5 常见检查脚本
#### HTTP 服务
使用 `curl` 或 `wget`:
```docker
## 使用 curl
HEALTHCHECK CMD curl -f http://localhost/ || exit 1
## 使用 wget(Alpine 默认包含)
HEALTHCHECK CMD wget -q --spider http://localhost/ || exit 1
```
#### 数据库
```docker
## MySQL
HEALTHCHECK CMD mysqladmin ping -h localhost || exit 1
## Redis
HEALTHCHECK CMD redis-cli ping || exit 1
```
#### 自定义脚本
```docker
COPY healthcheck.sh /usr/local/bin/
HEALTHCHECK CMD ["healthcheck.sh"]
```
---
### 7.12.6 在 Compose 中使用
可以在 `compose.yaml` (或 `docker-compose.yml`) 中覆盖或定义健康检查:
```yaml
services:
web:
image: nginx
healthcheck:
test: ["CMD", "curl", "-f", "http://localhost"]
interval: 1m30s
timeout: 10s
retries: 3
start_period: 40s
```
带健康检查的依赖启动:
```yaml
services:
web:
depends_on:
db:
condition: service_healthy # 等待 db 变健康才启动 web
db:
image: mysql
healthcheck:
test: ["CMD", "mysqladmin", "ping", "-h", "localhost"]
```
---
### 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",
"FailingStreak": 0,
"Log": [
{
"Start": "...",
"End": "...",
"ExitCode": 0,
"Output": "..."
}
]
}
```
---
### 7.12.8 最佳实践
#### 1. 避免副作用
健康检查会被频繁执行,不要在检查脚本中进行写操作或消耗大量资源的操作。
#### 2. 使用轻量级工具
优先使用镜像中已有的工具 (如 `wget`),避免为了健康检查安装庞大的依赖 (如 `curl`)。
#### 3. 设置合理的 Start Period
应用启动可能需要时间 (如 Java 应用)。设置 `--start-period` 可以防止在启动阶段因检查失败而误判。
```docker
## 给应用 1 分钟启动时间
HEALTHCHECK --start-period=60s CMD curl -f http://localhost/ || exit 1
```
#### 4. 只检查核心依赖
健康检查应主要关注 **当前服务** 是否可用,而不是检查其下游依赖 (数据库等)。下游依赖的检查应由应用逻辑处理。
---
================================================
FILE: 07_dockerfile/7.13_onbuild.md
================================================
## 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` 指令产生了临时文件,最好在同一个指令链中清理,或者提供机制让子镜像清理。
---
================================================
FILE: 07_dockerfile/7.14_label.md
================================================
## 7.14 LABEL 为镜像添加元数据
### 7.14.1 基本语法
```docker
LABEL <key>=<value> <key>=<value> ...
```
`LABEL` 指令以键值对的形式给镜像添加元数据。这些数据不会影响镜像的功能,但可以帮助用户理解镜像,或被自动化工具使用。
---
### 7.14.2 为什么需要 LABEL
1. **版本管理**:记录版本号、构建时间、Git Commit ID
2. **联系信息**:维护者邮箱、文档地址、支持渠道
3. **自动化工具**:CI/CD 工具可以读取标签触发操作
4. **许可证信息**:声明开源协议
---
### 7.14.3 基本用法
#### 定义单个标签
```docker
LABEL version="1.0"
LABEL description="这是一个 Web 应用服务器"
```
#### 定义多个标签:推荐
```docker
LABEL maintainer="user@example.com" \
version="1.2.0" \
description="My App Description" \
org.opencontainers.image.authors="Yeasy"
```
> 💡 包含空格的值需要用引号括起来。
---
### 7.14.4 常用标签规范
为了标准和互操作性,推荐使用 [OCI Image Format Specification](https://github.com/opencontainers/image-spec/blob/main/annotations.md#pre-defined-annotation-keys) 定义的标准标签:
| 标签 Key | 说明 | 示例 |
|----------|------|------|
| `org.opencontainers.image.created` | 构建时间(RFC 3339) | `2024-01-01T00:00:00Z` |
| `org.opencontainers.image.authors` | 作者/维护者 | `support@example.com` |
| `org.opencontainers.image.url` | 项目主页 | `https://example.com` |
| `org.opencontainers.image.documentation`| 文档地址 | `https://example.com/docs` |
| `org.opencontainers.image.source` | 源码仓库 | `https://github.com/user/repo` |
| `org.opencontainers.image.version` | 版本号 | `1.0.0` |
| `org.opencontainers.image.licenses` | 许可证 | `MIT` |
| `org.opencontainers.image.title` | 镜像标题 | `My App` |
| `org.opencontainers.image.description` | 描述 | `Production ready web server` |
#### 示例
```docker
LABEL org.opencontainers.image.authors="yeasy" \
org.opencontainers.image.documentation="https://yeasy.gitbooks.io" \
org.opencontainers.image.source="https://github.com/yeasy/docker_practice" \
org.opencontainers.image.licenses="MIT"
```
---
### 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` 使用,可以在构建时动态注入标签:
```docker
ARG BUILD_DATE
ARG VCS_REF
LABEL org.opencontainers.image.created=$BUILD_DATE \
org.opencontainers.image.revision=$VCS_REF
```
构建命令:
```bash
$ docker build \
--build-arg BUILD_DATE=$(date -u +'%Y-%m-%dT%H:%M:%SZ') \
--build-arg VCS_REF=$(git rev-parse --short HEAD) \
.
```
---
### 7.14.7 查看标签
#### docker inspect
查看镜像的标签信息:
```bash
$ docker inspect nginx --format '{{json .Config.Labels}}' | jq
{
"maintainer": "NGINX Docker Maintainers <docker-maint@nginx.com>"
}
```
#### 过滤器
可以使用标签过滤镜像:
```bash
## 列出作者是 yeasy 的所有镜像
$ docker images --filter "label=org.opencontainers.image.authors=yeasy"
## 删除所有带有特定标签的镜像
$ docker rmi $(docker images -q --filter "label=stage=builder")
```
---
================================================
FILE: 07_dockerfile/7.15_shell.md
================================================
## 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 格式时)。
---
### 7.15.2 为什么要用 SHELL 指令
#### 1. 使用 bash 特性
默认的 `/bin/sh` (通常是 dash 或 alpine 的 ash) 功能有限。如果你需要使用 bash 的特有功能 (如数组、`{}` 扩展、`pipefail` 等),可以切换 shell。
```docker
FROM ubuntu:24.04
## 切换到 bash
SHELL ["/bin/bash", "-c"]
## 现在可以使用 bash 特性了
RUN echo {a..z}
```
#### 2. 增强错误处理
默认情况下,管道命令 `cmd1 | cmd2` 只要 `cmd2` 成功,整个指令就视为成功。这可能掩盖构建错误。
```docker
## ❌ 这里的 wget 失败了,但构建继续(因为 tar 成功了)
RUN wget -O - https://invalid-url | tar xz
```
使用 `SHELL` 启用 `pipefail`:
```docker
## ✅ 启用 pipefail
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
## 如果 wget 失败,整个 RUN 就会失败
RUN wget -O - https://invalid-url | tar xz
```
#### 3. Windows 环境
在 Windows 容器中,经常需要在 `cmd` 和 `powershell` 之间切换。
```docker
FROM mcr.microsoft.com/windows/servercore:ltsc2022
## 默认是 cmd
RUN echo Default shell is cmd
## 切换到 powershell
SHELL ["powershell", "-command"]
RUN Write-Host "Hello from PowerShell"
## 切回 cmd
SHELL ["cmd", "/S", "/C"]
```
---
### 7.15.3 作用范围
`SHELL` 指令可以出现多次,每次只影响其后的指令:
```docker
FROM ubuntu:24.04
## 使用默认 sh
RUN echo "Using sh"
SHELL ["/bin/bash", "-c"]
## 使用 bash
RUN echo "Using bash"
SHELL ["/bin/sh", "-c"]
## 回到 sh
RUN echo "Using sh again"
```
---
### 7.15.4 对其他指令的影响
`SHELL` 影响的是所有使用 **shell 格式** 的指令:
| 指令格式 | 是否受 SHELL 影响 |
|---------|-------------------|
| `RUN command` | ✅ 是 |
| `RUN ["exec", "param"]` | ❌ 否 |
| `CMD command` | ✅ 是 |
| `CMD ["exec", "param"]` | ❌ 否 |
| `ENTRYPOINT command` | ✅ 是 |
| `ENTRYPOINT ["exec", "param"]` | ❌ 否 |
---
### 7.15.5 最佳实践
#### 1. 推荐开启 pipefail
对于使用 bash 的镜像,强烈建议开启 `pipefail`,以确保构建过程中的错误能被及时捕获。
```docker
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
```
#### 2. 明确意图
如果由于脚本需求必须更改 shell,最好在 Dockerfile 中显式声明,而不是依赖默认行为。
#### 3. 尽量保持一致
避免在 Dockerfile 中频繁切换 SHELL,这会使构建过程难以理解和调试。尽量在头部定义一次即可。
---
================================================
FILE: 07_dockerfile/7.16_references.md
================================================
## 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/
================================================
FILE: 07_dockerfile/7.17_multistage_builds.md
================================================
## 7.17 多阶段构建
在 Docker 17.05 版本之前,我们构建 Docker 镜像时,通常会采用两种方式:
### 7.17.1 全部放入一个 Dockerfile
一种方式是将所有的构建过程包含在一个 `Dockerfile` 中,包括项目及其依赖库的编译、测试、打包等流程,这里可能会带来的一些问题:
* 镜像层次多,镜像体积较大,部署时间变长
* 源代码存在泄露的风险
例如,编写 `app.go` 文件,该程序输出 `Hello World!`
```go
package main
import "fmt"
func main(){
fmt.Printf("Hello World!");
}
```
编写 `Dockerfile.one` 文件
```docker
FROM golang:alpine
RUN apk --no-cache add git ca-certificates
WORKDIR /go/src/github.com/go/helloworld/
COPY app.go .
RUN go mod init helloworld \
&& go get -d -v github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app . \
&& cp /go/src/github.com/go/helloworld/app /root
WORKDIR /root/
CMD ["./app"]
```
构建镜像
```bash
$ docker build -t go/helloworld:1 -f Dockerfile.one .
```
### 7.17.2 分散到多个 Dockerfile
另一种方式,就是我们事先在一个 `Dockerfile` 将项目及其依赖库编译测试打包好后,再将其拷贝到运行环境中,这种方式需要我们编写两个 `Dockerfile` 和一些编译脚本才能将其两个阶段自动整合起来,这种方式虽然可以很好地规避第一种方式存在的风险,但明显部署过程较复杂。
例如,编写 `Dockerfile.build` 文件
```docker
FROM golang:alpine
RUN apk --no-cache add git
WORKDIR /go/src/github.com/go/helloworld
COPY app.go .
RUN go get -d -v github.com/go-sql-driver/mysql \
&& CGO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -o app .
```
编写 `Dockerfile.copy` 文件
```docker
FROM alpine:latest
RUN apk --no-cache add ca-certificates
WORKDIR /root/
COPY app .
CMD ["./app"]
```
新建 `build.sh`
```bash
#!/bin/sh
echo Building go/helloworld:build
docker build -t go/helloworld:build . -f Dockerfile.build
docker create --name extract go/helloworld:b
gitextract_132u_zi8/ ├── .devcontainer/ │ └── devcontainer.json ├── .docker/ │ └── docker-entrypoint.sh ├── .editorconfig ├── .gitattributes ├── .github/ │ ├── CODEOWNERS │ ├── FUNDING.yml │ ├── ISSUE_TEMPLATE/ │ │ ├── Bug_report.md │ │ ├── Custom.md │ │ └── Feature_request.md │ ├── PULL_REQUEST_TEMPLATE.md │ ├── dependabot.yml │ └── workflows/ │ ├── check-link.yml │ ├── ci.yaml │ ├── dependabot-automerge.yml │ └── release-pdf.yml ├── .gitignore ├── .vuepress/ │ ├── .gitignore │ └── config.js ├── .zhlintignore ├── 01_introduction/ │ ├── 1.1_quickstart.md │ ├── 1.2_what.md │ ├── 1.3_why.md │ ├── README.md │ └── summary.md ├── 02_basic_concept/ │ ├── 2.1_image.md │ ├── 2.2_container.md │ ├── 2.3_repository.md │ ├── README.md │ └── summary.md ├── 03_install/ │ ├── 3.10_experimental.md │ ├── 3.1_ubuntu.md │ ├── 3.2_debian.md │ ├── 3.3_fedora.md │ ├── 3.4_centos.md │ ├── 3.5_raspberry-pi.md │ ├── 3.6_offline.md │ ├── 3.7_mac.md │ ├── 3.8_windows.md │ ├── 3.9_mirror.md │ ├── README.md │ └── summary.md ├── 04_image/ │ ├── 4.1_pull.md │ ├── 4.2_list.md │ ├── 4.3_rm.md │ ├── 4.4_commit.md │ ├── 4.5_build.md │ ├── 4.6_other.md │ ├── 4.7_internal.md │ ├── README.md │ ├── demo/ │ │ ├── buildkit/ │ │ │ ├── Dockerfile │ │ │ ├── Dockerfile.buildkit │ │ │ ├── aws.txt │ │ │ ├── package.json │ │ │ └── src/ │ │ │ └── index.js │ │ ├── multi-arch/ │ │ │ └── Dockerfile │ │ └── multistage-builds/ │ │ ├── .gitignore │ │ ├── Dockerfile │ │ ├── Dockerfile.build │ │ ├── Dockerfile.copy │ │ ├── Dockerfile.one │ │ ├── app.go │ │ └── build.sh │ └── summary.md ├── 05_container/ │ ├── 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 │ ├── README.md │ └── summary.md ├── 06_repository/ │ ├── 6.1_dockerhub.md │ ├── 6.2_registry.md │ ├── 6.3_registry_auth.md │ ├── 6.4_nexus3_registry.md │ ├── README.md │ ├── demo/ │ │ ├── auth/ │ │ │ └── nginx.htpasswd │ │ ├── config.yml │ │ ├── docker-compose.yml │ │ ├── root-ca.cnf │ │ ├── site.cnf │ │ └── ssl/ │ │ ├── docker.domain.com.crt │ │ └── docker.domain.com.key │ └── summary.md ├── 07_dockerfile/ │ ├── 7.10_workdir.md │ ├── 7.11_user.md │ ├── 7.12_healthcheck.md │ ├── 7.13_onbuild.md │ ├── 7.14_label.md │ ├── 7.15_shell.md │ ├── 7.16_references.md │ ├── 7.17_multistage_builds.md │ ├── 7.18_multistage_builds_laravel.md │ ├── 7.1_run.md │ ├── 7.2_copy.md │ ├── 7.3_add.md │ ├── 7.4_cmd.md │ ├── 7.5_entrypoint.md │ ├── 7.6_env.md │ ├── 7.7_arg.md │ ├── 7.8_volume.md │ ├── 7.9_expose.md │ ├── README.md │ ├── multistage_example/ │ │ └── laravel/ │ │ ├── .dockerignore │ │ ├── Dockerfile │ │ └── laravel.conf │ └── summary.md ├── 08_data/ │ ├── 8.1_volume.md │ ├── 8.2_bind-mounts.md │ ├── 8.3_tmpfs.md │ ├── README.md │ └── summary.md ├── 09_network/ │ ├── 9.1_dns.md │ ├── 9.2_network_types.md │ ├── 9.3_custom_network.md │ ├── 9.4_container_linking.md │ ├── 9.5_port_mapping.md │ ├── 9.6_network_isolation.md │ ├── 9.7_advanced_networking.md │ ├── README.md │ └── summary.md ├── 10_buildx/ │ ├── 10.1_buildkit.md │ ├── 10.2_buildx.md │ ├── 10.3_multi-arch-images.md │ ├── README.md │ └── summary.md ├── 11_compose/ │ ├── 11.1_introduction.md │ ├── 11.2_install.md │ ├── 11.3_usage.md │ ├── 11.4_commands.md │ ├── 11.5_compose_file.md │ ├── 11.6_django.md │ ├── 11.7_rails.md │ ├── 11.8_wordpress.md │ ├── 11.9_lnmp.md │ ├── README.md │ ├── demo/ │ │ ├── app/ │ │ │ ├── Dockerfile │ │ │ ├── app.py │ │ │ └── docker-compose.yml │ │ ├── django/ │ │ │ ├── .gitignore │ │ │ ├── Dockerfile │ │ │ ├── docker-compose.yml │ │ │ └── requirements.txt │ │ └── wordpress/ │ │ └── docker-compose.yml │ └── summary.md ├── 12_implementation/ │ ├── 12.1_arch.md │ ├── 12.2_namespace.md │ ├── 12.3_cgroups.md │ ├── 12.4_ufs.md │ ├── 12.5_container_format.md │ ├── 12.6_network.md │ ├── README.md │ └── summary.md ├── 13_kubernetes_concepts/ │ ├── 13.1_intro.md │ ├── 13.2_concepts.md │ ├── 13.3_design.md │ ├── 13.4_advanced.md │ ├── 13.5_practice.md │ ├── README.md │ └── summary.md ├── 14_kubernetes_setup/ │ ├── 14.1_kubeadm.md │ ├── 14.2_kubeadm-docker.md │ ├── 14.3_docker-desktop.md │ ├── 14.4_kind.md │ ├── 14.5_k3s.md │ ├── 14.6_systemd.md │ ├── 14.7_dashboard.md │ ├── 14.8_kubectl.md │ ├── README.md │ └── summary.md ├── 15_etcd/ │ ├── 15.1_intro.md │ ├── 15.2_install.md │ ├── 15.3_cluster.md │ ├── 15.4_etcdctl.md │ ├── README.md │ ├── demo/ │ │ └── cluster/ │ │ └── docker-compose.yml │ └── summary.md ├── 16_cloud/ │ ├── 16.1_intro.md │ ├── 16.2_tencentCloud.md │ ├── 16.3_alicloud.md │ ├── 16.4_aws.md │ ├── 16.5_multicloud.md │ ├── README.md │ └── summary.md ├── 17_ecosystem/ │ ├── 17.1_coreos_intro.md │ ├── 17.2_coreos_install.md │ ├── 17.3_podman.md │ ├── 17.4_buildah.md │ ├── 17.5_skopeo.md │ ├── 17.6_containerd.md │ ├── 17.7_secure_runtime.md │ ├── 17.8_wasm.md │ ├── README.md │ ├── coreos_README.md │ ├── demo/ │ │ └── example.fcc │ └── summary.md ├── 18_security/ │ ├── 18.1_kernel_ns.md │ ├── 18.2_control_group.md │ ├── 18.3_daemon_sec.md │ ├── 18.4_kernel_capability.md │ ├── 18.5_other_feature.md │ ├── 18.6_image_security.md │ ├── README.md │ └── summary.md ├── 19_observability/ │ ├── 19.1_prometheus.md │ ├── 19.2_elk.md │ ├── 19.3_performance_optimization.md │ ├── README.md │ └── summary.md ├── 20_cases_os/ │ ├── 20.1_busybox.md │ ├── 20.2_alpine.md │ ├── 20.3_debian.md │ ├── 20.4_centos.md │ ├── README.md │ └── summary.md ├── 21_case_devops/ │ ├── 21.1_devops_workflow.md │ ├── 21.2_github_actions.md │ ├── 21.3_drone.md │ ├── 21.4_drone_demo.md │ ├── 21.5_ide.md │ ├── 21.6_vsCode.md │ ├── 21.7_practical_examples.md │ ├── README.md │ ├── drone_demo.app.go │ ├── drone_demo.drone.yml │ ├── drone_docker-compose.yml │ ├── drone_env.example │ ├── drone_gitignore │ └── summary.md ├── CHANGELOG.md ├── CONTRIBUTING.md ├── README.md ├── SUMMARY.md ├── _config.yml ├── _images/ │ └── cover.sketch ├── appendix/ │ ├── README.md │ ├── _images/ │ │ ├── cmd_logic 2.graffle/ │ │ │ └── data.plist │ │ ├── cmd_logic.dot │ │ ├── cmd_logic.dot.bak │ │ ├── cmd_logic.graffle/ │ │ │ └── data.plist │ │ └── container_status.dot │ ├── best_practices.md │ ├── command/ │ │ ├── README.md │ │ ├── docker.md │ │ └── dockerd.md │ ├── debug.md │ ├── faq/ │ │ ├── README.md │ │ └── errors.md │ ├── glossary.md │ ├── learning_roadmap.md │ ├── repo/ │ │ ├── README.md │ │ ├── centos.md │ │ ├── minio.md │ │ ├── mongodb.md │ │ ├── mysql.md │ │ ├── nginx.md │ │ ├── nodejs.md │ │ ├── php.md │ │ ├── redis.md │ │ ├── ubuntu.md │ │ └── wordpress.md │ └── resources.md ├── book.json ├── docker-compose.yml └── package.json
SYMBOL INDEX (3 symbols across 3 files)
FILE: 04_image/demo/multistage-builds/app.go
function main (line 5) | func main(){
FILE: 11_compose/demo/app/app.py
function hello (line 8) | def hello():
FILE: 21_case_devops/drone_demo.app.go
function main (line 5) | func main(){
Condensed preview — 265 files, each showing path, character count, and a content snippet. Download the .json file or copy for the full structured content (638K chars).
[
{
"path": ".devcontainer/devcontainer.json",
"chars": 602,
"preview": "// https://code.visualstudio.com/docs/remote/devcontainerjson-reference\n\n{\n \"image\": \"yeasy/docker_practice:latest\",\n "
},
{
"path": ".docker/docker-entrypoint.sh",
"chars": 178,
"preview": "#!/usr/bin/env sh\n\necho\necho\necho \"Please open your browser: 127.0.0.1:4000\"\necho\necho \"欢迎加入 QQ 群:【 145983035 】 分享 Docke"
},
{
"path": ".editorconfig",
"chars": 305,
"preview": "# EditorConfig is awesome: https://EditorConfig.org\n\nroot = true\n\n[*]\n\nindent_style = space\n\nindent_size = 2\n\nend_of_lin"
},
{
"path": ".gitattributes",
"chars": 88,
"preview": "* text=auto eol=lf\n\n*.sh text eol=lf\n\n* linguist-language=go\n"
},
{
"path": ".github/CODEOWNERS",
"chars": 1382,
"preview": "* @yeasy @khs1994\n/.github/* @khs1994\n/.travis/* @khs1994\n/.vuepress/* "
},
{
"path": ".github/FUNDING.yml",
"chars": 731,
"preview": "# These are supported funding model platforms\n\ngithub: yeasy\npatreon: # Replace with a single Patreon username\nopen_coll"
},
{
"path": ".github/ISSUE_TEMPLATE/Bug_report.md",
"chars": 775,
"preview": "---\nname: Bug report\nabout: Create a report to help us improve\n\n---\n\n* [ ] Have u googled the problem? If no, pls do tha"
},
{
"path": ".github/ISSUE_TEMPLATE/Custom.md",
"chars": 779,
"preview": "---\nname: Custom issue template\nabout: Create a issue about Docker\n\n---\n\n* [ ] Have u googled the problem? If no, pls do"
},
{
"path": ".github/ISSUE_TEMPLATE/Feature_request.md",
"chars": 74,
"preview": "---\nname: Feature request\nabout: Suggest an idea for docker_practice\n\n---\n"
},
{
"path": ".github/PULL_REQUEST_TEMPLATE.md",
"chars": 350,
"preview": "<!--\n Thanks for your contribution.\n See [CONTRIBUTING](../CONTRIBUTING.md) for contribution guidelines.\n-->\n\n**Pr"
},
{
"path": ".github/dependabot.yml",
"chars": 504,
"preview": "version: 2\nupdates:\n - package-ecosystem: \"npm\"\n directory: \"/\"\n schedule:\n interval: \"weekly\"\n commit-me"
},
{
"path": ".github/workflows/check-link.yml",
"chars": 2221,
"preview": "name: Check link\n\non:\n workflow_dispatch:\n\njobs:\n check-link:\n name: check-link\n runs-on: ubuntu-latest\n step"
},
{
"path": ".github/workflows/ci.yaml",
"chars": 6088,
"preview": "name: CI\n\non:\n push:\n pull_request:\n workflow_dispatch:\n\ndefaults:\n run:\n shell: bash --noprofile --norc -exo pip"
},
{
"path": ".github/workflows/dependabot-automerge.yml",
"chars": 775,
"preview": "name: Dependabot auto-merge\non: pull_request\n\npermissions:\n contents: write\n pull-requests: write\n\njobs:\n dependabot:"
},
{
"path": ".github/workflows/release-pdf.yml",
"chars": 1371,
"preview": "name: Build PDF on Release\n\non:\n release:\n types: [published]\n workflow_dispatch:\n\npermissions:\n contents: write\n\n"
},
{
"path": ".gitignore",
"chars": 291,
"preview": "# Created by .gitignore support plugin (hsz.mobi)\n*.~\n*.tmp\n.idea/\n_book/\nformat_report.txt\n*.swp\n*.edx\n.DS_Store\n\nnode_"
},
{
"path": ".vuepress/.gitignore",
"chars": 26,
"preview": "/*\n!.gitignore\n!config.js\n"
},
{
"path": ".vuepress/config.js",
"chars": 11094,
"preview": "const { config } = require('vuepress-theme-hope')\n\nmodule.exports = config({\n title: 'Docker 从入门到实践',\n base: '/',\n he"
},
{
"path": ".zhlintignore",
"chars": 31,
"preview": "node_modules/\n.vuepress/\n.git/\n"
},
{
"path": "01_introduction/1.1_quickstart.md",
"chars": 965,
"preview": "## 1.1 快速上手\n\n本节将通过一个简单的 Web 应用例子,带你快速体验 Docker 的核心流程:构建镜像、运行容器。\n\n### 1.1.1 准备代码\n\n创建一个名为 `hello-docker` 的文件夹,并在其中创建一个 `in"
},
{
"path": "01_introduction/1.2_what.md",
"chars": 3317,
"preview": "## 1.2 什么是 Docker\n\nDocker 是彻底改变了软件开发和交付方式的革命性技术。本节将从核心概念、与传统虚拟机的对比、技术基础以及历史生态等多个维度,带你深入理解什么是 Docker。\n\n### 1.2.1 一句话理解 Do"
},
{
"path": "01_introduction/1.3_why.md",
"chars": 4195,
"preview": "## 1.3 为什么要用 Docker\n\n在回答 “为什么用 Docker” 之前,笔者想先问一个问题:**你有没有经历过这些场景?**\n\n### 1.3.1 没有 Docker 的世界\n\n在 Docker 出现之前,软件开发和运维面临着诸"
},
{
"path": "01_introduction/README.md",
"chars": 792,
"preview": "# 第一章 Docker 简介\n\n本章将带领你进入 **Docker** 的世界。\n\n> **版本提示**:本书内容及示例基于 **Docker Engine v29.x** 及以上版本。值得注意的是,自 Docker Engine v29"
},
{
"path": "01_introduction/summary.md",
"chars": 460,
"preview": "## 本章小结\n\n- Docker 是一种轻量级虚拟化技术,核心价值是 **环境一致性**\n- 与虚拟机相比,Docker 更轻量、更快速、资源利用率更高\n- Docker 基于 Linux 内核的 Namespace、Cgroups 和 "
},
{
"path": "02_basic_concept/2.1_image.md",
"chars": 4726,
"preview": "## 2.1 镜像\n\nDocker 镜像作为容器运行的基石,其设计理念和实现机制至关重要。本节将深入探讨镜像的本质、与操作系统的关系、内容构成以及核心的分层存储机制。\n\n### 2.1.1 一句话理解镜像\n\n> **Docker 镜像是一个"
},
{
"path": "02_basic_concept/2.2_container.md",
"chars": 4647,
"preview": "## 2.2 容器\n\n容器是 Docker 技术的核心,是应用实际运行的载体。本节将从容器的本质、与虚拟机的区别、存储层机制以及生命周期管理等方面,全面解析 Docker 容器。\n\n### 2.2.1 一句话理解容器\n\n> **容器是镜像的"
},
{
"path": "02_basic_concept/2.3_repository.md",
"chars": 6305,
"preview": "## 2.3 仓库\n\nDocker Registry 是镜像分发和管理的核心组件。本节将介绍 Registry 的基本概念、公共和私有服务的选择,以及镜像的安全管理。\n\n### 2.3.1 一句话理解 Registry\n\n> **Docke"
},
{
"path": "02_basic_concept/README.md",
"chars": 546,
"preview": "# 第二章 基本概念\n\n**Docker** 包括三个基本概念:\n\n* **镜像** (`Image`):Docker 镜像是一个特殊的文件系统,除了提供容器运行时所需的程序、库、资源、配置等文件外,还包含了一些为运行时准备的一些配置参数 "
},
{
"path": "02_basic_concept/summary.md",
"chars": 1482,
"preview": "## 本章小结\n\n本章介绍了 Docker 的三个核心概念:镜像、容器和仓库。\n\n| 概念 | 要点 |\n|------|------|\n| **镜像是什么** | 只读的应用模板,包含运行所需的一切 |\n| **分层存储** | 多层叠加"
},
{
"path": "03_install/3.10_experimental.md",
"chars": 307,
"preview": "## 3.10 开启实验特性\n\n一些 docker 命令或功能仅当 **实验特性** 开启时才能使用,请按照以下方法进行设置。\n\n### 3.10.1 Docker CLI 的实验特性\n\nCLI 的实验特性通常包含仍在开发中的新功能。幸运的"
},
{
"path": "03_install/3.1_ubuntu.md",
"chars": 4769,
"preview": "## 3.1 Ubuntu\n\nUbuntu 是 Docker 最常用的运行环境之一。本节将介绍如何在 Ubuntu 系统上安装 Docker,并配置国内镜像加速。\n\n> 警告:切勿在没有配置 Docker APT 源的情况下直接使用 apt"
},
{
"path": "03_install/3.2_debian.md",
"chars": 4866,
"preview": "## 3.2 Debian\n\nDebian 以其稳定性著称,是 Docker 的理想宿主系统。本节将指导你在 Debian 上完成 Docker 的安装。\n\n> 警告:切勿在没有配置 Docker APT 源的情况下直接使用 apt 命令安"
},
{
"path": "03_install/3.3_fedora.md",
"chars": 4314,
"preview": "## 3.3 Fedora\n\nFedora 作为技术前沿的 Linux 发行版,对 Docker 有着良好的支持。本节介绍在 Fedora 上的安装步骤。\n\n> 警告:切勿在没有配置 Docker dnf 源的情况下直接使用 dnf 命令安"
},
{
"path": "03_install/3.4_centos.md",
"chars": 5094,
"preview": "## 3.4 CentOS\n\nCentOS (及其替代品 Rocky Linux、AlmaLinux) 是企业级服务器常用的操作系统。本节介绍在这些系统上安装 Docker 的步骤。\n\n> 警告:切勿在没有配置 Docker YUM 源的情"
},
{
"path": "03_install/3.5_raspberry-pi.md",
"chars": 5958,
"preview": "## 3.5 Raspberry Pi\n\n树莓派等 ARM 架构设备在物联网和边缘计算领域应用广泛。本节介绍如何在树莓派上安装 Docker。\n\n> 警告:切勿在没有配置 Docker APT 源的情况下直接使用 apt 命令安装 Dock"
},
{
"path": "03_install/3.6_offline.md",
"chars": 7093,
"preview": "## 3.6 Linux 离线安装\n\n生产环境中一般都是没有公网资源的,本文介绍如何在生产服务器上离线部署 `Docker`\n\n括号内的字母表示该操作需要在哪些服务器上执行\n\n 要求系统最低为 macOS Sonoma 14."
},
{
"path": "03_install/3.8_windows.md",
"chars": 1644,
"preview": "## 3.8 Windows 10/11\n\n在 Windows 平台上,Docker Desktop 提供了完整的 Docker 开发环境。本节介绍在 Windows 10/11 上的安装和配置。\n\n### 3.8.1 系统要求\n\n[Doc"
},
{
"path": "03_install/README.md",
"chars": 472,
"preview": "# 第三章 安装 Docker\n\nDocker 分为 `stable` `test` 和 `nightly` 三个更新频道。\n\n官方网站上有各种环境下的[安装指南](https://docs.docker.com/get-docker/),"
},
{
"path": "03_install/summary.md",
"chars": 805,
"preview": "## 本章小结\n\nDocker 支持在多种平台上安装和使用,选择合适的安装方式是顺利使用 Docker 的第一步。\n\n| 平台 | 推荐方式 | 说明 |\n|------|---------|------|\n| **Ubuntu/Debia"
},
{
"path": "04_image/4.1_pull.md",
"chars": 4005,
"preview": "## 4.1 获取镜像\n\n从 Docker 镜像仓库获取镜像可谓是 Docker 运作的第一步。本节将介绍如何使用 `docker pull` 命令下载镜像,以及如何理解下载过程。\n\n### 4.1.1 docker pull 命令\n\n从镜"
},
{
"path": "04_image/4.2_list.md",
"chars": 4677,
"preview": "## 4.2 列出镜像\n\n在下载了镜像后,我们可以使用 `docker image ls` 命令列出本地主机上的镜像。\n\n### 4.2.1 基本用法\n\n查看本地已下载的镜像:\n\n```bash\n$ docker image ls\nREPO"
},
{
"path": "04_image/4.3_rm.md",
"chars": 4772,
"preview": "## 4.3 删除本地镜像\n\n当不再需要某个镜像时,我们可以将其删除以释放存储空间。本节介绍删除镜像的常用方法。\n\n### 4.3.1 基本用法\n\n使用 `docker image rm` 删除本地镜像:\n\n```bash\n$ docker"
},
{
"path": "04_image/4.4_commit.md",
"chars": 4663,
"preview": "## 4.4 利用 commit 理解镜像构成\n\n> 注意:如果您是初学者,您可以暂时跳过后面的内容,直接学习[容器](../05_container/)一节。\n\n注意:`docker commit` 命令除了学习之外,还有一些特殊的应用场"
},
{
"path": "04_image/4.5_build.md",
"chars": 7250,
"preview": "## 4.5 使用 Dockerfile 定制镜像\n\n从刚才的 `docker commit` 的学习中,我们可以了解到,镜像的定制实际上就是定制每一层所添加的配置、文件。如果我们可以把每一层修改、安装、构建、操作的命令都写入一个脚本,用这"
},
{
"path": "04_image/4.6_other.md",
"chars": 2399,
"preview": "## 4.6 其它制作镜像的方式\n\n除了标准的使用 `Dockerfile` 生成镜像的方法外,由于各种特殊需求和历史原因,还提供了一些其它方法用以生成镜像。\n\n### 4.6.1 从 rootfs 压缩包导入\n\n格式:`docker im"
},
{
"path": "04_image/4.7_internal.md",
"chars": 2123,
"preview": "## 4.7 实现原理\n\nDocker 镜像是怎么实现增量的修改和维护的?为什么容器启动如此之快?这一切都归功于 Docker 的镜像分层存储设计。\n\n### 4.7.1 镜像与分层存储\n\n在之前的章节中,我们一直强调镜像包含操作系统完整的"
},
{
"path": "04_image/README.md",
"chars": 715,
"preview": "# 第四章 使用镜像\n\n在之前的介绍中,我们知道镜像是 Docker 的三大组件之一。\n\nDocker 运行容器前需要本地存在对应的镜像,如果本地不存在该镜像,Docker 会从镜像仓库下载该镜像。\n\n## 本章内容\n\n本章将介绍更多关于镜"
},
{
"path": "04_image/demo/buildkit/Dockerfile",
"chars": 245,
"preview": "FROM node:alpine as builder\n\nWORKDIR /app\n\nCOPY package.json /app/\n\nRUN npm i --registry=https://registry.npmmirror.com "
},
{
"path": "04_image/demo/buildkit/Dockerfile.buildkit",
"chars": 1225,
"preview": "# syntax = docker/dockerfile:experimental\n\nFROM node:alpine as builder\n\nWORKDIR /app\n\nCOPY package.json /app/\n\nRUN --mou"
},
{
"path": "04_image/demo/buildkit/aws.txt",
"chars": 7,
"preview": "awskey\n"
},
{
"path": "04_image/demo/buildkit/package.json",
"chars": 192,
"preview": "{\n \"name\": \"my_app\",\n \"version\": \"19.6.0\",\n \"devDependencies\": {\n \"webpack\": \"*\",\n \"webpack-cli\": \"*\"\n },\n \"s"
},
{
"path": "04_image/demo/buildkit/src/index.js",
"chars": 16,
"preview": "console.log(1);\n"
},
{
"path": "04_image/demo/multi-arch/Dockerfile",
"chars": 80,
"preview": "FROM --platform=$TARGETPLATFORM alpine\n\nRUN uname -a > /os.txt\n\nCMD cat /os.txt\n"
},
{
"path": "04_image/demo/multistage-builds/.gitignore",
"chars": 4,
"preview": "app\n"
},
{
"path": "04_image/demo/multistage-builds/Dockerfile",
"chars": 388,
"preview": "FROM golang:alpine as builder\n\nRUN apk --no-cache add git\n\nWORKDIR /go/src/github.com/go/helloworld/\n\nRUN go get -d -v g"
},
{
"path": "04_image/demo/multistage-builds/Dockerfile.build",
"chars": 225,
"preview": "FROM golang:alpine\n\nRUN apk --no-cache add git\n\nWORKDIR /go/src/github.com/go/helloworld\n\nCOPY app.go .\n\nRUN go get -d -"
},
{
"path": "04_image/demo/multistage-builds/Dockerfile.copy",
"chars": 102,
"preview": "FROM alpine:latest\n\nRUN apk --no-cache add ca-certificates\n\nWORKDIR /root/\n\nCOPY app .\n\nCMD [\"./app\"]\n"
},
{
"path": "04_image/demo/multistage-builds/Dockerfile.one",
"chars": 326,
"preview": "FROM golang:alpine\n\nRUN apk --no-cache add git ca-certificates\n\nWORKDIR /go/src/github.com/go/helloworld/\n\nCOPY app.go ."
},
{
"path": "04_image/demo/multistage-builds/app.go",
"chars": 75,
"preview": "package main\n\nimport \"fmt\"\n\nfunc main(){\n fmt.Printf(\"Hello World!\");\n}\n"
},
{
"path": "04_image/demo/multistage-builds/build.sh",
"chars": 341,
"preview": "#!/bin/sh\n\necho Building go/helloworld:build\n\ndocker build -t go/helloworld:build . -f Dockerfile.build\n\ndocker create -"
},
{
"path": "04_image/summary.md",
"chars": 1151,
"preview": "## 本章小结\n\n本章介绍了 Docker 镜像的获取、列出、删除以及构建方式。\n\n| 操作 | 命令 |\n|------|------|\n| 拉取镜像 | `docker pull 镜像名:标签` |\n| 拉取所有标签 | `docker"
},
{
"path": "05_container/5.1_run.md",
"chars": 3407,
"preview": "## 5.1 启动\n\n本节将详细介绍 Docker 容器的启动方式,包括新建启动和重新启动已停止的容器。\n\n### 5.1.1 启动方式概述\n\n启动容器有两种方式:\n\n- **新建并启动**:基于镜像创建新容器\n- **重新启动**:将已终"
},
{
"path": "05_container/5.2_daemon.md",
"chars": 3778,
"preview": "## 5.2 守护态运行\n\n在生产环境中,我们通常需要容器持续运行,不受终端关闭的影响。本节将深入讲解如何让容器在后台运行,以及理解容器生命周期的核心概念。\n\n### 5.2.1 核心概念:前台 vs 后台\n\n当你在终端运行一个程序时,有两"
},
{
"path": "05_container/5.3_stop.md",
"chars": 3126,
"preview": "## 5.3 终止\n\n本节将介绍如何终止一个运行中的容器,以及几种不同的终止方式及其区别。\n\n### 5.3.1 终止方式概述\n\n终止容器有三种方式:\n\n| 方式 | 命令 | 说明 |\n|------|------|------|\n| *"
},
{
"path": "05_container/5.4_attach_exec.md",
"chars": 4392,
"preview": "## 5.4 进入容器\n\n### 5.4.1 为什么需要进入容器\n\n使用 `-d` 参数启动容器后,容器在后台运行。以下场景需要进入容器内部操作:\n\n| 场景 | 示例 |\n|------|------|\n| **调试问题** | 查看日志"
},
{
"path": "05_container/5.5_import_export.md",
"chars": 1097,
"preview": "## 5.5 导出和导入\n\n当我们需要迁移容器或者备份容器时,可以使用 Docker 的导入和导出功能。本节将介绍这两个命令的使用方法。\n\n### 5.5.1 导出容器\n\n如果要导出本地某个容器,可以使用 `docker export` 命"
},
{
"path": "05_container/5.6_rm.md",
"chars": 3199,
"preview": "## 5.6 删除\n\n随着容器的创建和停止,系统中会积累大量的容器。本节将介绍如何删除不再需要的容器,以及如何清理所有停止的容器。\n\n### 5.6.1 基本用法\n\n使用 `docker rm` 删除已停止的容器:\n\n```bash\n$ d"
},
{
"path": "05_container/README.md",
"chars": 300,
"preview": "# 第五章 操作容器\n\n容器是 Docker 又一核心概念。\n\n简单的说,容器是独立运行的一个或一组应用,以及它们的运行态环境。对应的,虚拟机可以理解为模拟运行的一整套操作系统 (提供了运行态环境和其他系统环境) 和跑在上面的应用。\n\n本章"
},
{
"path": "05_container/summary.md",
"chars": 1117,
"preview": "## 本章小结\n\n本章介绍了 Docker 容器的启动、停止、进入和删除等核心操作。\n\n| 操作 | 命令 | 说明 |\n|------|------|------|\n| 新建并运行 | `docker run` | 最常用的启动方式 |\n"
},
{
"path": "06_repository/6.1_dockerhub.md",
"chars": 2803,
"preview": "## 6.1 Docker Hub\n\n### 6.1.1 什么是 Docker Hub\n\nDocker Hub 是 Docker 的中央镜像仓库,通过它您可以轻松地分享和获取 Docker 镜像。\n\n\n[Docker Hub](https:"
},
{
"path": "06_repository/6.2_registry.md",
"chars": 3417,
"preview": "## 6.2 私有仓库\n\n有时候使用 Docker Hub 这样的公共仓库可能不方便,用户可以创建一个本地仓库供私人使用。\n\n本节介绍如何使用本地仓库。\n\n[Docker Registry](https://docs.docker.com/"
},
{
"path": "06_repository/6.3_registry_auth.md",
"chars": 4410,
"preview": "## 6.3 私有仓库高级配置\n\n上一节我们搭建了一个具有基础功能的私有仓库,本小节我们来使用 `Docker Compose` 搭建一个拥有权限认证、TLS 的私有仓库。\n\n新建一个文件夹,以下步骤均在该文件夹中进行。\n\n### 6.3."
},
{
"path": "06_repository/6.4_nexus3_registry.md",
"chars": 3764,
"preview": "## 6.4 Nexus 3\n\n使用 Docker 官方的 Registry 创建的仓库面临一些维护问题。比如某些镜像删除以后空间默认是不会回收的,需要一些命令去回收空间然后重启 Registry。在企业中把内部的一些工具包放入 `Nexu"
},
{
"path": "06_repository/README.md",
"chars": 375,
"preview": "# 第六章 访问仓库\n\n仓库 (`Repository`) 是集中存放镜像的地方。\n\n一个容易混淆的概念是注册服务器 (`Registry`)。实际上注册服务器是管理仓库的具体服务器,每个服务器上可以有多个仓库,而每个仓库下面有多个镜像。从"
},
{
"path": "06_repository/demo/auth/nginx.htpasswd",
"chars": 71,
"preview": "username:$2y$05$TRWvCC6ilpKpY3ICifw32Ok3.8SpG3etq8O5WGdCm9wvyDhtSbRgy\n\n"
},
{
"path": "06_repository/demo/config.yml",
"chars": 691,
"preview": "version: 0.1\nlog:\n accesslog:\n disabled: true\n level: debug\n formatter: text\n fields:\n service: registry\n e"
},
{
"path": "06_repository/demo/docker-compose.yml",
"chars": 188,
"preview": "\n\nservices:\n registry:\n image: registry:2\n ports:\n - \"443:5000\"\n volumes:\n - ./:/etc/docker/registry"
},
{
"path": "06_repository/demo/root-ca.cnf",
"chars": 140,
"preview": "[root_ca]\nbasicConstraints = critical,CA:TRUE,pathlen:1\nkeyUsage = critical, nonRepudiation, cRLSign, keyCertSign\nsubjec"
},
{
"path": "06_repository/demo/site.cnf",
"chars": 244,
"preview": "[server]\nauthorityKeyIdentifier=keyid,issuer\nbasicConstraints = critical,CA:FALSE\nextendedKeyUsage=serverAuth\nkeyUsage ="
},
{
"path": "06_repository/demo/ssl/docker.domain.com.crt",
"chars": 2139,
"preview": "-----BEGIN CERTIFICATE-----\nMIIF/zCCA+egAwIBAgIJAMbgVbFo7I6IMA0GCSqGSIb3DQEBCwUAMHoxCzAJBgNV\nBAYTAkNOMQ8wDQYDVQQIDAZTaGF"
},
{
"path": "06_repository/demo/ssl/docker.domain.com.key",
"chars": 3247,
"preview": "-----BEGIN RSA PRIVATE KEY-----\nMIIJKgIBAAKCAgEA1Hbm1tZvAeC8J54pQLHTVCtrICQ8KFTLOZakPHSWox8iQ4i2\nfkZaSccvE/51LIFnCM1yyZv"
},
{
"path": "06_repository/summary.md",
"chars": 459,
"preview": "## 本章小结\n\n本章介绍了 Docker Hub 的使用、私有仓库的搭建以及 Nexus 3 等企业级方案。\n\n| 功能 | 说明 |\n|------|------|\n| **官方镜像** | 优先使用的基础镜像 |\n| **拉取限制**"
},
{
"path": "07_dockerfile/7.10_workdir.md",
"chars": 2247,
"preview": "## 7.10 WORKDIR 指定工作目录\n\n### 7.10.1 基本语法\n\n```docker\nWORKDIR <工作目录路径>\n```\n\n`WORKDIR` 指定后续指令的工作目录。如果目录不存在,Docker 会自动创建。\n\n--"
},
{
"path": "07_dockerfile/7.11_user.md",
"chars": 3615,
"preview": "## 7.11 USER 指定当前用户\n\n### 7.11.1 基本语法\n\n```docker\nUSER <用户名>[:<用户组>]\nUSER <UID>[:<GID>]\n```\n\n`USER` 指令切换后续指令 (RUN、CMD、ENTR"
},
{
"path": "07_dockerfile/7.12_healthcheck.md",
"chars": 2955,
"preview": "## 7.12 HEALTHCHECK 健康检查\n\n### 7.12.1 基本语法\n\n```docker\nHEALTHCHECK [选项] CMD <命令>\nHEALTHCHECK NONE\n```\n\n`HEALTHCHECK` 指令告诉 "
},
{
"path": "07_dockerfile/7.13_onbuild.md",
"chars": 1990,
"preview": "## 7.13 ONBUILD 为他人作嫁衣裳\n\n### 7.13.1 基本语法\n\n```docker\nONBUILD <其它指令>\n```\n\n`ONBUILD` 是一个特殊的指令,它后面跟的是其它指令 (如 `RUN`,`COPY` 等)"
},
{
"path": "07_dockerfile/7.14_label.md",
"chars": 2778,
"preview": "## 7.14 LABEL 为镜像添加元数据\n\n### 7.14.1 基本语法\n\n```docker\nLABEL <key>=<value> <key>=<value> ...\n```\n\n`LABEL` 指令以键值对的形式给镜像添加元数据。"
},
{
"path": "07_dockerfile/7.15_shell.md",
"chars": 2016,
"preview": "## 7.15 SHELL 指令\n\n### 7.15.1 基本语法\n\n```docker\nSHELL [\"executable\", \"parameters\"]\n```\n\n`SHELL` 指令允许覆盖 Docker 默认的 shell。\n\n-"
},
{
"path": "07_dockerfile/7.16_references.md",
"chars": 765,
"preview": "## 7.16 参考文档\n\n### 官方文档\n\n* `Dockerfile` 官方参考手册:https://docs.docker.com/engine/reference/builder/\n\n* `Dockerfile` 最佳实践指南:h"
},
{
"path": "07_dockerfile/7.17_multistage_builds.md",
"chars": 3404,
"preview": "## 7.17 多阶段构建\n\n在 Docker 17.05 版本之前,我们构建 Docker 镜像时,通常会采用两种方式:\n\n### 7.17.1 全部放入一个 Dockerfile\n\n一种方式是将所有的构建过程包含在一个 `Dockerf"
},
{
"path": "07_dockerfile/7.18_multistage_builds_laravel.md",
"chars": 5137,
"preview": "## 7.18 实战多阶段构建 Laravel 镜像\n\n> 本节适用于 PHP 开发者阅读。`Laravel` 基于 8.x 版本,各个版本的文件结构可能会有差异,请根据实际自行修改。\n\n### 7.18.1 准备\n\n新建一个 `Larav"
},
{
"path": "07_dockerfile/7.1_run.md",
"chars": 2425,
"preview": "## 7.1 RUN 执行命令\n\n### 7.1.1 基本语法\n\n```docker\nRUN <command>\nRUN [\"executable\", \"param1\", \"param2\"]\n```\n\n`RUN` 指令是 Dockerfil"
},
{
"path": "07_dockerfile/7.2_copy.md",
"chars": 3656,
"preview": "## 7.2 COPY 复制文件\n\n### 7.2.1 基本语法\n\n```docker\nCOPY [选项] <源路径>... <目标路径>\nCOPY [选项] [\"<源路径1>\", \"<源路径2>\", ... \"<目标路径>\"]\n```\n\n"
},
{
"path": "07_dockerfile/7.3_add.md",
"chars": 2825,
"preview": "## 7.3 ADD 更高级的复制文件\n\n### 7.3.1 基本语法\n\n```docker\nADD [选项] <源路径>... <目标路径>\nADD [选项] [\"<源路径>\", ... \"<目标路径>\"]\n```\n\n`ADD` 在 `C"
},
{
"path": "07_dockerfile/7.4_cmd.md",
"chars": 3796,
"preview": "## 7.4 CMD 容器启动命令\n\n### 7.4.1 什么是 CMD\n\n`CMD` 指令用于指定容器启动时默认执行的命令。它定义了容器的 “主进程”。\n\n> **核心概念**:容器的生命周期 = 主进程的生命周期。CMD 指定的命令就是"
},
{
"path": "07_dockerfile/7.5_entrypoint.md",
"chars": 5113,
"preview": "## 7.5 ENTRYPOINT 入口点\n\n### 7.5.1 什么是 ENTRYPOINT\n\n`ENTRYPOINT` 指定容器启动时运行的入口程序。与 CMD 不同,ENTRYPOINT 定义的命令不会被 `docker run` 的"
},
{
"path": "07_dockerfile/7.6_env.md",
"chars": 3360,
"preview": "## 7.6 ENV 设置环境变量\n\n### 7.6.1 基本语法\n\n```docker\n## 格式一:单个变量\n\nENV <key> <value>\n\n## 格式二:多个变量(推荐)\n\nENV <key1>=<value1> <key2>"
},
{
"path": "07_dockerfile/7.7_arg.md",
"chars": 3254,
"preview": "## 7.7 ARG 构建参数\n\n### 7.7.1 基本语法\n\n```docker\nARG <参数名>[=<默认值>]\n```\n\n`ARG` 指令定义构建时的变量,可以在 `docker build` 时通过 `--build-arg` "
},
{
"path": "07_dockerfile/7.8_volume.md",
"chars": 3373,
"preview": "## 7.8 VOLUME 定义匿名卷\n\n### 7.8.1 基本语法\n\n```docker\nVOLUME [\"/路径1\", \"/路径2\"]\nVOLUME /路径\n```\n\n`VOLUME` 指令创建挂载点,并标记为外部挂载的卷。\n\n---"
},
{
"path": "07_dockerfile/7.9_expose.md",
"chars": 2300,
"preview": "## 7.9 EXPOSE 暴露端口\n\n### 7.9.1 基本语法\n\n```docker\nEXPOSE <端口> [<端口>/<协议>...]\n```\n\n`EXPOSE` 声明容器运行时提供服务的端口。这是一个 **文档性质的声明**,告"
},
{
"path": "07_dockerfile/README.md",
"chars": 1463,
"preview": "# 第七章 Dockerfile 指令详解\n\n## 什么是 Dockerfile\n\nDockerfile 是一个文本文件,其内包含了一条条的 **指令 (Instruction)**,每一条指令构建一层,因此每一条指令的内容,就是描述该层应"
},
{
"path": "07_dockerfile/multistage_example/laravel/.dockerignore",
"chars": 160,
"preview": ".idea/\n.git/\n\nvendor/\n\nnode_modules/\n\npublic/js/\npublic/css/\npublic/mix-manifest.json\n\nyarn-error.log\n\nbootstrap/cache/*"
},
{
"path": "07_dockerfile/multistage_example/laravel/Dockerfile",
"chars": 1592,
"preview": "FROM node:alpine as frontend\n\nCOPY package.json /app/\n\nRUN set -x ; cd /app \\\n && npm install --registry=https://re"
},
{
"path": "07_dockerfile/multistage_example/laravel/laravel.conf",
"chars": 372,
"preview": "server {\n listen 80 default_server;\n root /app/laravel/public;\n index index.php index.html;\n\n location / {\n try"
},
{
"path": "07_dockerfile/summary.md",
"chars": 1228,
"preview": "## 本章小结\n\n本章详细介绍了 Dockerfile 的所有核心指令,以下是各指令要点的速查表。\n\n| 指令 | 作用 | 关键要点 |\n|------|------|---------|\n| **FROM** | 指定基础镜像 | 必须"
},
{
"path": "08_data/8.1_volume.md",
"chars": 5801,
"preview": "## 8.1 数据卷\n\n### 8.1.1 为什么需要数据卷\n\n容器的存储层有一个关键问题:**容器删除后,数据就没了**。\n\n```mermaid\nflowchart LR\n Run[容器运行] --> Write[写入数据]\n "
},
{
"path": "08_data/8.2_bind-mounts.md",
"chars": 4522,
"preview": "## 8.2 挂载主机目录\n\n### 8.2.1 什么是绑定挂载\n\nBind Mount (绑定挂载) 将 **宿主机的目录或文件** 直接挂载到容器中。容器可以读写宿主机的文件系统。\n\n```mermaid\nflowchart LR\n "
},
{
"path": "08_data/8.3_tmpfs.md",
"chars": 747,
"preview": "## 8.3 tmpfs 挂载\n\n`tmpfs` 挂载会把数据放在宿主机内存中,而不是写入容器可写层或数据卷。\n\n### 8.3.1 适用场景\n\n- 临时缓存\n- 会话数据\n- 不希望落盘的敏感中间文件\n\n### 8.3.2 基本用法\n\n使"
},
{
"path": "08_data/README.md",
"chars": 251,
"preview": "# 第八章 数据管理\n\n如图 8-1 所示,Docker 数据管理主要围绕三类挂载方式展开。\n\n\n\n图 8-1:Docker 数据挂载类型示意图\n"
},
{
"path": "08_data/summary.md",
"chars": 953,
"preview": "## 本章小结\n\n本章介绍了 Docker 的三种数据管理方式:数据卷 (Volume)、绑定挂载 (Bind Mount) 和 tmpfs 挂载。\n\n| 方式 | 特点 | 适用场景 |\n|------|------|---------|"
},
{
"path": "09_network/9.1_dns.md",
"chars": 2047,
"preview": "## 9.1 配置 DNS\n\nDocker 1.10.0 以后,内建了一个 DNS 服务器,使得容器可以直接通过容器名称通信。方法很简单,只要在创建容器时使用 `--name` 为容器命名即可。\n\n但是使用 Docker DNS 有个前提条"
},
{
"path": "09_network/9.2_network_types.md",
"chars": 1732,
"preview": "## 9.2 网络类型\n\nDocker 提供了多种网络驱动来满足不同的使用场景。安装 Docker 后,系统会自动创建三个默认网络:\n\n```bash\n$ docker network ls\nNETWORK ID NAME "
},
{
"path": "09_network/9.3_custom_network.md",
"chars": 1815,
"preview": "## 9.3 自定义网络\n\n在生产环境中,推荐使用用户自定义网络代替默认的 bridge 网络。自定义网络提供了更好的隔离性和服务发现能力。\n\n### 9.3.1 为什么要用自定义网络\n\n默认 bridge 网络存在以下局限,而自定义网络可"
},
{
"path": "09_network/9.4_container_linking.md",
"chars": 1005,
"preview": "## 9.4 容器互联\n\n容器之间的网络通信是 Docker 网络的核心功能之一。本节介绍容器互联的几种方式。\n\n### 9.4.1 同一网络内的容器\n\n同一自定义网络内的容器可以直接通过容器名通信,这是推荐的容器互联方式:\n\n```bas"
},
{
"path": "09_network/9.5_port_mapping.md",
"chars": 2424,
"preview": "## 9.5 外部访问容器\n\n容器运行在自己的隔离网络环境中 (通常是 Bridge 模式)。为了让外部网络访问容器内的服务,我们需要将容器的端口映射到宿主机的端口。\n\n### 9.5.1 为什么要映射端口\n\n容器的网络访问规则如下:\n\n-"
},
{
"path": "09_network/9.6_network_isolation.md",
"chars": 1390,
"preview": "## 9.6 网络隔离\n\nDocker 网络提供了天然的隔离能力,不同网络之间的容器默认无法通信。这是 Docker 网络安全的重要基础。\n\n### 9.6.1 网络隔离原理\n\n不同网络之间默认隔离,容器只能与同一网络中的容器直接通信:\n\n"
},
{
"path": "09_network/9.7_advanced_networking.md",
"chars": 12387,
"preview": "## 9.7 容器网络高级特性\n\n深入探讨容器网络的核心机制、Overlay 网络、CNI 插件生态、容器 DNS 解析、网络策略等高级特性,为生产级别的网络架构打下坚实基础。\n\n### 9.7.1 Overlay 网络原理与配置\n\nOve"
},
{
"path": "09_network/README.md",
"chars": 892,
"preview": "# 第九章 网络配置\n\nDocker 容器需要网络来与外部世界通信、容器之间相互通信以及与宿主机通信。Docker 在安装时会自动配置网络基础设施,大多数情况下开箱即用。\n\n## 概述\n\nDocker 启动时自动创建以下网络组件:\n\n```"
},
{
"path": "09_network/summary.md",
"chars": 874,
"preview": "## 本章小结\n\n本章介绍了 Docker 网络配置的各个方面:\n\n| 概念 | 要点 |\n|------|------|\n| **DNS 配置** | 自定义网络支持嵌入式 DNS,可通过容器名解析 |\n| **网络类型** | brid"
},
{
"path": "10_buildx/10.1_buildkit.md",
"chars": 4458,
"preview": "## 10.1 BuildKit\n\n**BuildKit** 是下一代的镜像构建组件,在 [moby/buildkit](https://github.com/moby/buildkit) 开源。\n\n> **重要**:自 Docker 23"
},
{
"path": "10_buildx/10.2_buildx.md",
"chars": 1260,
"preview": "## 10.2 使用 buildx 构建镜像\n\n### 10.2.1 使用\n\nBuildx 的使用非常直观,绝大多数情况下可以替代 `docker build` 命令。\n\n你可以直接使用 `docker buildx build` 命令构建"
},
{
"path": "10_buildx/10.3_multi-arch-images.md",
"chars": 3323,
"preview": "## 10.3 使用 buildx 构建多种系统架构支持的 Docker 镜像\n\nDocker 镜像可以支持多种系统架构,这意味着你可以在 `x86_64`、`arm64` 等不同架构的机器上运行同一个镜像。这是通过一个名为 “manife"
},
{
"path": "10_buildx/README.md",
"chars": 786,
"preview": "# 第十章 Docker Buildx\n\nDocker Buildx 是一个 docker CLI 插件,其扩展了 docker 命令,支持 [Moby BuildKit](10.1_buildkit.md) 提供的功能。提供了与 dock"
},
{
"path": "10_buildx/summary.md",
"chars": 760,
"preview": "## 本章小结\n\nDocker Buildx 是 Docker 构建系统的重要进化,提供了高效、安全且支持多平台的镜像构建能力。\n\n| 概念 | 要点 |\n|------|------|\n| **BuildKit** | 下一代构建引擎,D"
},
{
"path": "11_compose/11.1_introduction.md",
"chars": 1506,
"preview": "## 11.1 简介\n\n`Compose` 项目是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。从功能上看,跟 `OpenStack` 中的 `Heat` 十分类似。\n\n其代码目前在 [docker/comp"
},
{
"path": "11_compose/11.2_install.md",
"chars": 1223,
"preview": "## 11.2 安装与卸载\n\n`Compose` 是 Docker 官方的开源项目,负责实现对 Docker 容器集群的快速编排。\n\n从 `v2` 版本开始,`Compose` 作为 `docker` 的子命令存在,例如 `docker c"
},
{
"path": "11_compose/11.3_usage.md",
"chars": 1637,
"preview": "## 11.3 使用\n\n本节将通过一个具体的 Web 应用案例,介绍 Docker Compose 的基本概念和使用方法。\n\n### 11.3.1 术语\n\n首先介绍几个术语。\n\n* 服务 (`service`):一个应用容器,实际上可以运行"
},
{
"path": "11_compose/11.4_commands.md",
"chars": 5398,
"preview": "## 11.4 命令说明\n\nDocker Compose 提供了丰富的命令来管理项目和容器。本节将详细介绍这些命令的使用格式和常用选项。\n\n### 11.4.1 命令对象与格式\n\n对于 Compose 来说,大部分命令的对象既可以是项目本身"
},
{
"path": "11_compose/11.5_compose_file.md",
"chars": 8238,
"preview": "## 11.5 Compose 模板文件\n\n模板文件是使用 `Compose` 的核心,涉及到的指令关键字也比较多。但大家不用担心,这里面大部分指令跟 `docker run` 相关参数的含义都是类似的。\n\n默认的模板文件名称为 `comp"
},
{
"path": "11_compose/11.6_django.md",
"chars": 7387,
"preview": "## 11.6 实战 Django\n\n> 本小节内容适合 `Python` 开发人员阅读。\n\n本节将使用 Docker Compose 配置并运行一个 **Django + PostgreSQL** 应用。笔者不仅会介绍具体步骤,还会解释每"
},
{
"path": "11_compose/11.7_rails.md",
"chars": 5142,
"preview": "## 11.7 实战 Rails\n\n> 本小节内容适合 Ruby 开发人员阅读。\n\n本节使用 Docker Compose 配置并运行一个 **Rails + PostgreSQL** 应用。\n\n### 11.7.1 架构概览\n\n如图 11"
},
{
"path": "11_compose/11.8_wordpress.md",
"chars": 3484,
"preview": "## 11.8 实战 WordPress\n\nWordPress 是全球最流行的内容管理系统 (CMS)。使用 Docker Compose 可以在几分钟内搭建一个包含数据库、Web 服务和持久化存储的生产级 WordPress 环境。\n\n-"
},
{
"path": "11_compose/11.9_lnmp.md",
"chars": 749,
"preview": "## 11.9 实战 LNMP\n\n### 什么是 LNMP\n\nLNMP 是一个经典的 Web 应用栈,由以下四个开源软件组合而成:\n\n- **L**:Linux(操作系统)\n- **N**:Nginx(Web 服务器)\n- **M**:My"
},
{
"path": "11_compose/README.md",
"chars": 592,
"preview": "# 第十一章 Docker Compose\n\n`Docker Compose` 是 Docker 官方编排 (Orchestration) 项目之一,负责快速的部署分布式应用。\n\n> ⚠️ **重要提示:Compose V1 已停止支持**"
},
{
"path": "11_compose/demo/app/Dockerfile",
"chars": 103,
"preview": "FROM python:3.12-alpine\nADD . /code\nWORKDIR /code\nRUN pip install redis flask\nCMD [\"python\", \"app.py\"]\n"
},
{
"path": "11_compose/demo/app/app.py",
"chars": 295,
"preview": "from flask import Flask\nfrom redis import Redis\n\napp = Flask(__name__)\nredis = Redis(host='redis', port=6379)\n\n@app.rout"
},
{
"path": "11_compose/demo/app/docker-compose.yml",
"chars": 98,
"preview": "\nservices:\n\n web:\n build: .\n ports:\n - \"5000:5000\"\n\n redis:\n image: \"redis:alpine\"\n"
},
{
"path": "11_compose/demo/django/.gitignore",
"chars": 25,
"preview": "django_example\nmanage.py\n"
},
{
"path": "11_compose/demo/django/Dockerfile",
"chars": 144,
"preview": "FROM python:3\nENV PYTHONUNBUFFERED 1\nRUN mkdir /code\nWORKDIR /code\nADD requirements.txt /code/\nRUN pip install -r requir"
},
{
"path": "11_compose/demo/django/docker-compose.yml",
"chars": 225,
"preview": "\nservices:\n\n db:\n image: postgres\n environment:\n POSTGRES_PASSWORD: 'postgres'\n\n web:\n build: .\n comm"
},
{
"path": "11_compose/demo/django/requirements.txt",
"chars": 38,
"preview": "Django>=5.0.6,<6.0\npsycopg2>=2.7,<3.0\n"
},
{
"path": "11_compose/demo/wordpress/docker-compose.yml",
"chars": 697,
"preview": "\nservices:\n\n db:\n image: mysql:8.0\n command:\n - --default_authentication_plugin=mysql_native_password\n "
},
{
"path": "11_compose/summary.md",
"chars": 810,
"preview": "## 本章小结\n\nDocker Compose 是管理多容器应用的利器,通过 YAML 文件声明式地定义服务、网络和数据卷。\n\n| 概念 | 要点 |\n|------|------|\n| **核心概念** | 服务 (service) 和项"
},
{
"path": "12_implementation/12.1_arch.md",
"chars": 2724,
"preview": "## 12.1 基本架构\n\nDocker 的架构设计简洁而高效,主要由客户端和服务端两部分组成。\n\n### 12.1.1 核心架构图\n\nDocker 采用了 **C/S (客户端/服务端)** 架构。Client 向 Daemon 发送请求"
},
{
"path": "12_implementation/12.2_namespace.md",
"chars": 5184,
"preview": "## 12.2 命名空间\n\n命名空间是 Linux 内核一个强大的特性。每个容器都有自己单独的命名空间,运行在其中的应用都像是在独立的操作系统中运行一样。命名空间保证了容器之间彼此互不影响。\n\n### 12.2.1 什么是 Namespac"
},
{
"path": "12_implementation/12.3_cgroups.md",
"chars": 4856,
"preview": "## 12.3 控制组\n\n控制组 (Cgroups) 是 Linux 内核提供的另一种关键机制,主要用于资源的限制和审计。\n\n### 12.3.1 什么是控制组\n\n控制组 (Control Groups,简称 cgroups) 是 Linu"
},
{
"path": "12_implementation/12.4_ufs.md",
"chars": 4884,
"preview": "## 12.4 联合文件系统\n\n联合文件系统 (UnionFS) 是 Docker 镜像分层存储的基础,它允许将多个目录挂载为同一个虚拟文件系统。\n\n### 12.4.1 什么是联合文件系统\n\n联合文件系统 (UnionFS) 是一种 **"
},
{
"path": "12_implementation/12.5_container_format.md",
"chars": 951,
"preview": "## 12.5 容器格式\n\n### Docker 容器格式的演进\n\n最初,Docker 采用了 `LXC` 中的容器格式。从 0.7 版本以后开始去除 LXC 的依赖,转而使用自行开发的 [libcontainer](https://git"
},
{
"path": "12_implementation/12.6_network.md",
"chars": 2358,
"preview": "## 12.6 网络\n\nDocker 的网络实现其实就是利用了 Linux 上的网络命名空间和虚拟网络设备 (特别是 veth pair)。建议先熟悉了解这两部分的基本概念再阅读本章。\n\n### 12.6.1 基本原理\n\n首先,要实现网络通"
},
{
"path": "12_implementation/README.md",
"chars": 831,
"preview": "# 第十二章 底层实现\n\nDocker 底层的核心技术包括 Linux 上的命名空间 (Namespaces)、控制组 (Control groups)、Union 文件系统 (Union file systems) 和容器格式 (Cont"
},
{
"path": "12_implementation/summary.md",
"chars": 1264,
"preview": "## 本章小结\n\n本章深入介绍了 Docker 的底层实现,包括命名空间、控制组和联合文件系统三大核心技术。\n\n| 技术 | 作用 | 要点 |\n|------|------|------|\n| **Namespace** | 资源隔离 |"
},
{
"path": "13_kubernetes_concepts/13.1_intro.md",
"chars": 1809,
"preview": "## 13.1 简介\n\n如图 13-1 所示,Kubernetes 使用舵手图标作为项目标识。\n\n\n\n图 13-1:Kubernetes 项目标识"
},
{
"path": "13_kubernetes_concepts/13.2_concepts.md",
"chars": 6107,
"preview": "## 13.2 基本概念\n\n如图 13-2 所示,Kubernetes 由控制平面与工作节点构成。\n\n\n\n图 13-2:Kubern"
},
{
"path": "13_kubernetes_concepts/13.3_design.md",
"chars": 1395,
"preview": "## 13.3 架构设计\n\n任何优秀的项目都离不开优秀的架构设计。本小节将介绍 Kubernetes 在架构方面的设计考虑。\n\n### 13.3.1 基本考虑\n\n如果让我们自己从头设计一套容器管理平台,有如下几个方面是很容易想到的:\n\n* "
},
{
"path": "13_kubernetes_concepts/13.4_advanced.md",
"chars": 1531,
"preview": "## 13.4 高级特性\n\n掌握了 Kubernetes 的核心概念 (Pod,Service,Deployment) 后,我们需要了解更多高级特性以构建生产级应用。\n\n### 13.4.1 Helm - 包管理工具\n\n[Helm](htt"
},
{
"path": "13_kubernetes_concepts/13.5_practice.md",
"chars": 1430,
"preview": "## 13.5 实战练习\n\n本章将通过一个具体的案例:部署一个 Nginx 网站,并为其配置 Service 和 Ingress,来串联前面学到的知识。\n\n### 13.5.1 目标\n\n1. 部署一个 Nginx Deployment。\n"
},
{
"path": "13_kubernetes_concepts/README.md",
"chars": 386,
"preview": "# 第十三章 容器编排基础\n\n`Kubernetes` 是 Google 发起的开源容器编排系统,它支持多种云平台与私有数据中心。\n\n`Kubernetes` 负责对容器工作负载进行调度与编排,其目的是让用户通过集群声明式地管理应用,而无需"
},
{
"path": "13_kubernetes_concepts/summary.md",
"chars": 728,
"preview": "## 本章小结\n\nKubernetes 是当前最主流的容器编排平台,其声明式管理模型和丰富的 API 为大规模容器化应用提供了坚实的基础。\n\n| 概念 | 要点 |\n|------|------|\n| **Pod** | 最小调度单位,包含"
},
{
"path": "14_kubernetes_setup/14.1_kubeadm.md",
"chars": 11676,
"preview": "## 14.1 使用 kubeadm 部署 Kubernetes\n\n`kubeadm` 提供了 `kubeadm init` 以及 `kubeadm join` 这两个命令,作为快速创建 `Kubernetes` 集群的最佳实践。\n\n> *"
},
{
"path": "14_kubernetes_setup/14.2_kubeadm-docker.md",
"chars": 7573,
"preview": "## 14.2 使用 kubeadm 部署 Kubernetes:使用 Docker\n\n`kubeadm` 提供了 `kubeadm init` 以及 `kubeadm join` 这两个命令,作为快速创建 `Kubernetes` 集群的"
},
{
"path": "14_kubernetes_setup/14.3_docker-desktop.md",
"chars": 514,
"preview": "## 14.3 在 Docker Desktop 使用\n\n使用 Docker Desktop 可以很方便的启用 Kubernetes。\n\n### 14.3.1 启用 Kubernetes\n\n在 Docker Desktop 设置页面,点击 "
},
{
"path": "14_kubernetes_setup/14.4_kind.md",
"chars": 1311,
"preview": "## 14.4 Kind - Kubernetes IN Docker\n\n[Kind](https://kind.sigs.k8s.io/) (Kubernetes in Docker) 是一个使用 Docker 容器作为节点运行本地 Ku"
},
{
"path": "14_kubernetes_setup/14.5_k3s.md",
"chars": 1119,
"preview": "## 14.5 K3s - 轻量级 Kubernetes\n\n[K3s](https://k3s.io/) 是一个轻量级的 Kubernetes 发行版,由 Rancher Labs 开发。它专为边缘计算、物联网、CI、ARM 等资源受限的环"
},
{
"path": "14_kubernetes_setup/14.6_systemd.md",
"chars": 1252,
"preview": "## 14.6 一步步部署 Kubernetes 集群\n\n### 概述\n\n部署 Kubernetes 集群涉及多个组件的安装和配置,包括 Master 节点和 Worker 节点。本章介绍如何使用 systemd 管理这些服务的生命周期。\n"
},
{
"path": "14_kubernetes_setup/14.7_dashboard.md",
"chars": 1192,
"preview": "## 14.7 部署 Dashboard\n\n[Kubernetes Dashboard](https://github.com/kubernetes/dashboard) 是基于网页的 Kubernetes 用户界面。\n\n 是 Kubernetes 自带的客户端,可以用它来直接操作 Kubern"
},
{
"path": "14_kubernetes_setup/README.md",
"chars": 755,
"preview": "# 第十四章 部署 Kubernetes\n\n目前,Kubernetes 支持在多种环境下使用,包括本地主机 (Ubuntu、Debian、CentOS、Fedora 等)、云服务 ([腾讯云](https://cloud.tencent.c"
},
{
"path": "14_kubernetes_setup/summary.md",
"chars": 609,
"preview": "## 本章小结\n\n部署 Kubernetes 集群有多种方式,应根据使用场景选择合适的方案。\n\n| 部署方式 | 适用场景 | 特点 |\n|---------|---------|------|\n| **kubeadm** | 生产环境 |"
},
{
"path": "15_etcd/15.1_intro.md",
"chars": 973,
"preview": "## 15.1 简介\n\n如图 15-1:所示,etcd 项目使用该标识。\n\n\n\n图 15-1:etcd 项目标识\n\n`etcd` 是 `CoreOS` 团队于 2013 "
},
{
"path": "15_etcd/15.2_install.md",
"chars": 2422,
"preview": "## 15.2 安装\n\n本节将介绍 etcd 的几种常见安装方式,包括二进制安装、Docker 镜像运行以及在 macOS 上的安装。\n\n`etcd` 基于 `Go` 语言实现,因此,用户可以从[项目主页](https://github.c"
},
{
"path": "15_etcd/15.3_cluster.md",
"chars": 3168,
"preview": "## 15.3 集群\n\n下面我们使用 [Docker Compose](../11_compose/README.md) 模拟启动一个 3 节点的 `etcd` 集群。\n\n编辑 `compose.yaml` (或 `docker-compo"
},
{
"path": "15_etcd/15.4_etcdctl.md",
"chars": 4415,
"preview": "## 15.4 使用 etcdctl\n\n`etcdctl` 是一个命令行客户端,它能提供一些简洁的命令,供用户直接跟 `etcd` 服务打交道,而无需基于 `HTTP API` 方式。这在某些情况下将很方便,例如用户对服务进行测试或者手动修"
},
{
"path": "15_etcd/README.md",
"chars": 241,
"preview": "# 第十五章 Etcd 项目\n\n`etcd` 是 `CoreOS` 团队发起的一个管理配置信息和服务发现 (`Service Discovery`) 的项目,在这一章里面,我们将基于 `etcd 3.x` 版本介绍该项目的目标,安装和使用,"
},
{
"path": "15_etcd/demo/cluster/docker-compose.yml",
"chars": 2665,
"preview": "version: \"3.6\"\nservices:\n\n node1:\n image: quay.io/coreos/etcd:v3.4.0\n volumes:\n - node1-data:/etcd-data\n "
},
{
"path": "15_etcd/summary.md",
"chars": 607,
"preview": "## 本章小结\n\netcd 是 Kubernetes 的核心存储组件,为分布式系统提供可靠的键值存储和服务发现能力。\n\n| 概念 | 要点 |\n|------|------|\n| **定位** | 分布式键值存储系统,用于配置管理和服务发现"
},
{
"path": "16_cloud/16.1_intro.md",
"chars": 630,
"preview": "## 16.1 简介\n\n随着容器技术的普及,目前主流的云计算服务商都提供了成熟的容器服务。与容器相关的云计算服务主要分为以下几种类型:\n\n### 16.1.1 容器编排托管服务\n\n这是目前最主流的形式。云厂商托管 Kubernetes 的控"
},
{
"path": "16_cloud/16.2_tencentCloud.md",
"chars": 4657,
"preview": "## 16.2 腾讯云\n\n如图 16-1:所示,腾讯云提供完整的云基础设施与容器能力。\n\n\n\n图 16-1:腾讯云标识\n\n[腾讯云](https://cloud.tencen"
},
{
"path": "16_cloud/16.3_alicloud.md",
"chars": 5407,
"preview": "## 16.3 阿里云\n\n如图 16-3:所示,阿里云是国内主流云服务平台之一。\n\n\n\n图 16-3:阿里云标识\n\n[阿里云](https://www.aliyun.com/"
},
{
"path": "16_cloud/16.4_aws.md",
"chars": 716,
"preview": "## 16.4 亚马逊云\n\n如图 16-5:所示,AWS 是全球主流云服务平台之一。\n\n\n\n图 16-5:AWS 标识\n\n[AWS](https://www.amazonaws.c"
},
{
"path": "16_cloud/16.5_multicloud.md",
"chars": 1183,
"preview": "## 16.5 多云部署策略\n\n企业在选择容器云平台时,通常会在 AWS EKS,Azure AKS,Google GKE 以及国内的阿里云 ACK,腾讯云 TKE 之间进行权衡。\n\n### 16.5.1 三大公有云 Kubernetes "
},
{
"path": "16_cloud/README.md",
"chars": 414,
"preview": "# 第十六章 容器与云计算\n\nDocker 目前已经得到了众多公有云平台的支持,并成为除虚拟机之外的核心云业务。\n\n除了 AWS、Google、Azure 等,国内的各大公有云厂商,基本上都同时支持了虚拟机服务和基于 Kubernetes "
},
{
"path": "16_cloud/summary.md",
"chars": 391,
"preview": "## 本章小结\n\n本章介绍了公有云服务对 Docker 的积极支持,以及新出现的容器云平台。\n\n事实上,Docker 技术的出现自身就极大推动了云计算行业的发展。\n\n通过整合公有云的虚拟机和 Docker 方式,可能获得更多的好处,包括\n\n"
},
{
"path": "17_ecosystem/17.1_coreos_intro.md",
"chars": 1126,
"preview": "## 17.1 Fedora CoreOS 简介\n\n[Fedora CoreOS](https://getfedora.org/coreos/) 是一个自动更新的,最小的,整体的,以容器为中心的操作系统,不仅适用于集群,而且可独立运行,并针"
},
{
"path": "17_ecosystem/17.2_coreos_install.md",
"chars": 788,
"preview": "## 17.2 Fedora CoreOS 安装\n\n### 17.2.1 下载 ISO\n\n在[下载页面](https://getfedora.org/coreos/download/) `Bare Metal & Virtualized` "
},
{
"path": "17_ecosystem/17.3_podman.md",
"chars": 1794,
"preview": "## 17.3 Podman - 下一代 Linux 容器工具\n\n[Podman](https://github.com/containers/podman) 是一个无守护进程、与 Docker 命令高度兼容的下一代 Linux 容器工具。"
},
{
"path": "17_ecosystem/17.4_buildah.md",
"chars": 2063,
"preview": "## 17.4 Buildah - 容器镜像构建工具\n\n本节介绍 Buildah,包括其基础概念、应用场景以及基本指令。\n\n### Buildah 简介\n\nBuildah 是一个用于构建 OCI(Open Container Initiat"
},
{
"path": "17_ecosystem/17.5_skopeo.md",
"chars": 1871,
"preview": "## 17.5 Skopeo - 容器镜像管理工具\n\n本节介绍 Skopeo,包括其基础概念、应用场景以及基本指令。\n\n### Skopeo 简介\n\nSkopeo 是一个由 Red Hat 赞助开源的命令行工具,它可以在不需要运行容器守护进"
},
{
"path": "17_ecosystem/17.6_containerd.md",
"chars": 2372,
"preview": "## 17.6 containerd - 核心容器运行时\n\n本节介绍 containerd,它是现代容器技术栈中最为核心的基础组件之一。了解 containerd 有助于更深入地理解 Docker 和 Kubernetes 的底层运行机制。"
},
{
"path": "17_ecosystem/17.7_secure_runtime.md",
"chars": 2466,
"preview": "## 17.7 安全容器运行时\n\n本节介绍容器技术生态中的安全运行时机制,主要探讨在隔离性和安全性上比标准 Linux 容器更进一步的方案,重点介绍 Kata Containers 和 gVisor。\n\n### 为什么需要安全容器?\n\n标准"
},
{
"path": "17_ecosystem/17.8_wasm.md",
"chars": 2185,
"preview": "## 17.8 WebAssembly 与容器\n\n本节介绍 WebAssembly (简写为 Wasm) 以及它为何成为现代容器生态中备受瞩目的前沿技术路线。\n\n### 什么是 WebAssembly?\n\n[WebAssembly (Was"
},
{
"path": "17_ecosystem/README.md",
"chars": 569,
"preview": "# 第十七章 容器其它生态\n\n本章将介绍 Docker 和 Kubernetes 之外的容器生态技术。\n\n## 本章内容\n\n* [Fedora CoreOS 简介](17.1_coreos_intro.md)\n * 专为容器化工作负载设计"
},
{
"path": "17_ecosystem/coreos_README.md",
"chars": 321,
"preview": "## Fedora CoreOS\n\n`CoreOS` 是一个专门为安全和大规模运行容器化工作负载而构建的新 Fedora 版本,它继承了 Fedora Atomic Host 和 CoreOS Container Linux 的优势。\n\n`"
},
{
"path": "17_ecosystem/demo/example.fcc",
"chars": 116,
"preview": "variant: fcos\nversion: 1.0.0\npasswd:\n users:\n - name: core\n ssh_authorized_keys:\n - ssh-rsa AAAA...\n"
},
{
"path": "17_ecosystem/summary.md",
"chars": 1045,
"preview": "## 本章小结\n\nDocker 并非容器生态的唯一选择,了解其他工具有助于根据场景做出合适的技术选型。\n\n| 项目 | 定位 | 特点 |\n|------|------|------|\n| **Fedora CoreOS** | 容器化操作"
},
{
"path": "18_security/18.1_kernel_ns.md",
"chars": 2243,
"preview": "## 18.1 内核命名空间\n\n命名空间 (Namespace) 是 Linux 容器隔离的基础,它确保了容器内的进程无法直接干扰主机或其他容器。虽然在本书第 12 章中我们已经从底层实现的角度介绍了 Namespace,但在本节中,我们将"
},
{
"path": "18_security/18.2_control_group.md",
"chars": 2074,
"preview": "## 18.2 控制组\n\n控制组 (Cgroups) 是 Linux 容器机制的另外一个关键组件。如果说命名空间 (Namespace) 决定了容器能 **看到** 什么,那么控制组就决定了容器能 **使用** 多少资源。\n\n在安全领域中,"
},
{
"path": "18_security/18.3_daemon_sec.md",
"chars": 2313,
"preview": "## 18.3 服务端防护\n\nDocker 守护进程(`dockerd`)是容器生命周期的核心驱动力。默认情况下,Docker 服务的运行需要极高的系统特权(root 权限),因此其安全性关系到整台宿主机的生死存亡。\n\n如果 Docker "
},
{
"path": "18_security/18.4_kernel_capability.md",
"chars": 1900,
"preview": "## 18.4 内核能力机制\n\n传统 Linux 的权限模型非常粗放:进程分为“特权进程”(以 root 用户 `UID 0` 运行)和“非特权进程”(其他 UID 运行)。这带来了一个致命问题——只要一个后台服务需要一个微小的特权(例如绑"
},
{
"path": "18_security/18.5_other_feature.md",
"chars": 2985,
"preview": "## 18.5 其它安全特性\n\n除了上述的命名空间、控制组以及能力机制,Linux 内核与云原生生态还提供了大量安全增强功能,它们共同筑成了一道防御纵深的“马奇诺防线”。本节主要介绍强制访问控制、系统调用拦截以及自动化的容器漏洞扫描技术。\n"
}
]
// ... and 65 more files (download for full content)
About this extraction
This page contains the full source code of the yeasy/docker_practice GitHub repository, extracted and formatted as plain text for AI agents and large language models (LLMs). The extraction includes 265 files (576.9 KB), approximately 251.0k tokens, and a symbol index with 3 extracted functions, classes, methods, constants, and types. Use this with OpenClaw, Claude, ChatGPT, Cursor, Windsurf, or any other AI tool that accepts text input. You can copy the full output to your clipboard or download it as a .txt file.
Extracted by GitExtract — free GitHub repo to text converter for AI. Built by Nikandr Surkov.