mirror of
https://github.com/yeasy/docker_practice.git
synced 2026-03-13 21:31:09 +00:00
Compare commits
1019 Commits
v0.6
...
40ded62baa
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
40ded62baa | ||
|
|
89b0dc4425 | ||
|
|
330e084e00 | ||
|
|
e62b203f1a | ||
|
|
cb6bf74a2e | ||
|
|
a980e34276 | ||
|
|
3d33e00802 | ||
|
|
1a820c8c8b | ||
|
|
585b364574 | ||
|
|
a0a5de7f11 | ||
|
|
83ba6b7b47 | ||
|
|
53f20dede7 | ||
|
|
48c8b50cf7 | ||
|
|
70ef2cba58 | ||
|
|
f5cfa4140a | ||
|
|
d174cf327c | ||
|
|
6a9ce44c5a | ||
|
|
c09f66da55 | ||
|
|
635e05ad34 | ||
|
|
6f22d0f5f0 | ||
|
|
17517e26b7 | ||
|
|
9fcaff6673 | ||
|
|
a096947382 | ||
|
|
be09a95d0d | ||
|
|
3af007b176 | ||
|
|
551dcfd2cb | ||
|
|
0d2654fbf2 | ||
|
|
3894ba56bc | ||
|
|
0dd0d036a2 | ||
|
|
a9c4189c0c | ||
|
|
56deeaee0f | ||
|
|
0ff67cc893 | ||
|
|
70cab9e92d | ||
|
|
67547c0c3e | ||
|
|
0d012c2d27 | ||
|
|
1eddef954e | ||
|
|
6baf2c68f2 | ||
|
|
fd28d966ca | ||
|
|
ba0c388384 | ||
|
|
fc4f1ce2d0 | ||
|
|
276eb356a2 | ||
|
|
246fc35c8e | ||
|
|
745b7c812a | ||
|
|
ad3b86d727 | ||
|
|
16e884da51 | ||
|
|
5bd2c37c54 | ||
|
|
19aaad591c | ||
|
|
bbfc9d34ff | ||
|
|
ecab788013 | ||
|
|
dd449bc84f | ||
|
|
d6e0838807 | ||
|
|
c44cbfcd62 | ||
|
|
c6e2ab8a4a | ||
|
|
669da56662 | ||
|
|
4941ef171c | ||
|
|
572266b2f4 | ||
|
|
4ca47b0ea1 | ||
|
|
24d26c0797 | ||
|
|
e57704271d | ||
|
|
92ea9623b2 | ||
|
|
b9ac198f19 | ||
|
|
185da0cf88 | ||
|
|
8965f084eb | ||
|
|
175aaae48a | ||
|
|
33af380be1 | ||
|
|
2ab40eacc0 | ||
|
|
6aa7a51fef | ||
|
|
79ac9c639a | ||
|
|
ad68b2d973 | ||
|
|
5ae315c9dd | ||
|
|
47cfc173a6 | ||
|
|
063c3f1381 | ||
|
|
f68795f34c | ||
|
|
acc74f17bb | ||
|
|
4fa2fd8543 | ||
|
|
95572d3d47 | ||
|
|
e58bd39de5 | ||
|
|
aa589406b9 | ||
|
|
0835f8467a | ||
|
|
bae82e993a | ||
|
|
b44c9acd6c | ||
|
|
63377d0431 | ||
|
|
e669ee0fe8 | ||
|
|
fdb879dcf2 | ||
|
|
b4b0d4160a | ||
|
|
99e0eb14ce | ||
|
|
b92ccc8309 | ||
|
|
cbfe75fe9b | ||
|
|
fec2e506d9 | ||
|
|
c58f61dbed | ||
|
|
8a0230e493 | ||
|
|
16e0d3502d | ||
|
|
02898e346c | ||
|
|
7d8d7e63f4 | ||
|
|
cc6510d0b2 | ||
|
|
306a205314 | ||
|
|
5eed72afa0 | ||
|
|
6bba22679b | ||
|
|
24eb615f54 | ||
|
|
43e12058eb | ||
|
|
8bdb8406f9 | ||
|
|
e08a34bcdf | ||
|
|
6e6d31d1d6 | ||
|
|
5c3841dc98 | ||
|
|
61a71f3c25 | ||
|
|
39207f57cf | ||
|
|
3a9d4bff61 | ||
|
|
4bc29c0300 | ||
|
|
65bc905920 | ||
|
|
1b7d941fcd | ||
|
|
e0ff3d315a | ||
|
|
09de77b543 | ||
|
|
344fc8bd3c | ||
|
|
7ec5772432 | ||
|
|
355dd3b051 | ||
|
|
ca011164c2 | ||
|
|
0f2956a646 | ||
|
|
da7991660e | ||
|
|
391c6364a0 | ||
|
|
99433b2e45 | ||
|
|
32c6a91aa9 | ||
|
|
bea7b8d3f4 | ||
|
|
1be1db5a43 | ||
|
|
7052cea8a3 | ||
|
|
584087ad46 | ||
|
|
dd6b022429 | ||
|
|
46020ff786 | ||
|
|
3ccba8afb4 | ||
|
|
f5e6a67664 | ||
|
|
439264a87f | ||
|
|
41558e73b4 | ||
|
|
f34a985a78 | ||
|
|
bd8387083a | ||
|
|
8d129d0ead | ||
|
|
85c5a280a4 | ||
|
|
98044d04b2 | ||
|
|
57873b1143 | ||
|
|
6acf209b9a | ||
|
|
ed4b3efd3b | ||
|
|
eb327bf908 | ||
|
|
cc683fdb56 | ||
|
|
13b8e12f44 | ||
|
|
ce4428adfb | ||
|
|
6443ddc07c | ||
|
|
ce5ae6a360 | ||
|
|
35b264694b | ||
|
|
e76b0b66a0 | ||
|
|
d80f3430b2 | ||
|
|
d9c5d2fb43 | ||
|
|
fe7530c3f6 | ||
|
|
30f00323e0 | ||
|
|
93ea51ff07 | ||
|
|
85be3008fa | ||
|
|
88f9183fd5 | ||
|
|
1c4a43e34e | ||
|
|
7014e4d87c | ||
|
|
18028b8eaa | ||
|
|
7c78d1c256 | ||
|
|
d4c6e590c6 | ||
|
|
1e415ac76c | ||
|
|
664ac88c85 | ||
|
|
734079661d | ||
|
|
1ed8c2c81f | ||
|
|
b9455b2169 | ||
|
|
10a77adbe6 | ||
|
|
31f3540fbc | ||
|
|
d5ce7f048b | ||
|
|
a613c78c33 | ||
|
|
d1d4f0f47d | ||
|
|
d4fc0a9da1 | ||
|
|
8dfdbc5b07 | ||
|
|
5caddf4413 | ||
|
|
95aed0099e | ||
|
|
533f30a939 | ||
|
|
1787c392c6 | ||
|
|
ca29ab51b1 | ||
|
|
64b75e997f | ||
|
|
31ea892dfc | ||
|
|
985e295d70 | ||
|
|
330beb4fdb | ||
|
|
baeb222455 | ||
|
|
8705cede17 | ||
|
|
ba36c90b6c | ||
|
|
96fc5acc75 | ||
|
|
6cfc12eece | ||
|
|
da614e8441 | ||
|
|
1e577a8c6f | ||
|
|
9fe6c356f0 | ||
|
|
aa08e32003 | ||
|
|
bc43b30964 | ||
|
|
7afb8991d3 | ||
|
|
6f810c89f3 | ||
|
|
0a87d2a643 | ||
|
|
8dd3141c57 | ||
|
|
ce48a157d5 | ||
|
|
ea00e0d82a | ||
|
|
5ebd6fb6f8 | ||
|
|
6265d0274b | ||
|
|
d1480092bf | ||
|
|
d414505835 | ||
|
|
aa3d56fce9 | ||
|
|
2cd0fac44b | ||
|
|
fb162df033 | ||
|
|
cc726421c8 | ||
|
|
47e83e7aa3 | ||
|
|
da10b3a7a1 | ||
|
|
9d62668fcf | ||
|
|
da09321b0e | ||
|
|
2b7f954c65 | ||
|
|
7b5a21bf6d | ||
|
|
e8d45d3be9 | ||
|
|
7d51bacb12 | ||
|
|
00274b0615 | ||
|
|
0b1812aac8 | ||
|
|
17ad494537 | ||
|
|
8e8126756a | ||
|
|
f9182e5d19 | ||
|
|
0fdc2d64a9 | ||
|
|
bcc901ac9f | ||
|
|
47ec0e2ab8 | ||
|
|
c572a399f3 | ||
|
|
fd85d5a0e5 | ||
|
|
1b2c480a55 | ||
|
|
fc8b8867af | ||
|
|
ff1f48a793 | ||
|
|
4751e59a6f | ||
|
|
f6ec3b23d2 | ||
|
|
1c932ef567 | ||
|
|
68d85ac31e | ||
|
|
83929dd096 | ||
|
|
d071ed7b99 | ||
|
|
58c09788f6 | ||
|
|
cbaa24c48f | ||
|
|
5d91c5a39f | ||
|
|
48785525e0 | ||
|
|
6fcf302809 | ||
|
|
4f08e73bfe | ||
|
|
4f7b448f0c | ||
|
|
c1c7a7981d | ||
|
|
cc210f75a7 | ||
|
|
b0ccb74dbd | ||
|
|
90a7316322 | ||
|
|
6ceeec7f45 | ||
|
|
d1e303d139 | ||
|
|
4b70a27151 | ||
|
|
e021b27323 | ||
|
|
eac2a57633 | ||
|
|
c25c7283cf | ||
|
|
4682ba336f | ||
|
|
ee26243625 | ||
|
|
5fb17c90e0 | ||
|
|
6eb603a33b | ||
|
|
9bc245dfdc | ||
|
|
9a4660492c | ||
|
|
edf1a3a07b | ||
|
|
acb8662c43 | ||
|
|
85b2325513 | ||
|
|
cf7d53dc9c | ||
|
|
83fbb87fd1 | ||
|
|
61bda4b259 | ||
|
|
c815720349 | ||
|
|
4aa20ecce3 | ||
|
|
6efe0297fe | ||
|
|
6c668a94df | ||
|
|
911ce1a2e4 | ||
|
|
8b186fb7e7 | ||
|
|
139b0ca65c | ||
|
|
67213bc2f5 | ||
|
|
d4d09f5154 | ||
|
|
f0568ebffd | ||
|
|
ddec6641b9 | ||
|
|
4d3d2d21fe | ||
|
|
f580130be0 | ||
|
|
acc5c6e723 | ||
|
|
1316e41222 | ||
|
|
9cfd1fda2b | ||
|
|
683cf95ca7 | ||
|
|
8952dee0a8 | ||
|
|
bdc8bd618c | ||
|
|
41da49b127 | ||
|
|
18ab3069a3 | ||
|
|
0fd981acc2 | ||
|
|
4561243c23 | ||
|
|
ff28d7219d | ||
|
|
34b6b617b9 | ||
|
|
e0797515d3 | ||
|
|
a691aa607a | ||
|
|
e9dc8dc974 | ||
|
|
44fdf58ba5 | ||
|
|
bd74c85bb7 | ||
|
|
e38ea82f37 | ||
|
|
c887661c08 | ||
|
|
fced7854ee | ||
|
|
e4a68ea7e4 | ||
|
|
d8e5cfe097 | ||
|
|
5634eb2fda | ||
|
|
c720d6959e | ||
|
|
3a24901664 | ||
|
|
ca5502e208 | ||
|
|
d2f5e120ae | ||
|
|
d887c8bda6 | ||
|
|
1e026fb3a1 | ||
|
|
33c2b2216d | ||
|
|
6c0fc9e416 | ||
|
|
a2e8595c42 | ||
|
|
5577c9caa6 | ||
|
|
2f46c5180c | ||
|
|
9a0a76a5bb | ||
|
|
9d450ffc26 | ||
|
|
e77c9ecbab | ||
|
|
386b1e4cf6 | ||
|
|
9f0b2c725f | ||
|
|
0dcc67da1c | ||
|
|
bbfe14e0af | ||
|
|
59317624bb | ||
|
|
2092debcc0 | ||
|
|
3ba77a7470 | ||
|
|
fbfab11293 | ||
|
|
f940d22950 | ||
|
|
a601a83c0e | ||
|
|
0ac24829e1 | ||
|
|
a85ae7da1e | ||
|
|
83619ad6e3 | ||
|
|
8e38beee0e | ||
|
|
c574178a02 | ||
|
|
e02f015a23 | ||
|
|
fde5496bb8 | ||
|
|
e1961d62eb | ||
|
|
c836c65a78 | ||
|
|
5cb92f63cf | ||
|
|
99e470eb2a | ||
|
|
61492ff11a | ||
|
|
6f96aaf5a6 | ||
|
|
709c77adcb | ||
|
|
3ba7560b4d | ||
|
|
f44a3ee402 | ||
|
|
eb274b16f6 | ||
|
|
fadf84bb69 | ||
|
|
c788212aad | ||
|
|
7690686ddb | ||
|
|
a3647ec3a3 | ||
|
|
008f01592b | ||
|
|
3923858f00 | ||
|
|
b11cac80fb | ||
|
|
38e810a407 | ||
|
|
224fbe9fed | ||
|
|
f260e3ba88 | ||
|
|
f17f29bf41 | ||
|
|
21a811d265 | ||
|
|
803b93c1fc | ||
|
|
4873267f31 | ||
|
|
1aef2b7734 | ||
|
|
49ef715e99 | ||
|
|
dd060d026a | ||
|
|
96aa9b99de | ||
|
|
4e84715725 | ||
|
|
48718d6035 | ||
|
|
8666d2683f | ||
|
|
84c2183cc8 | ||
|
|
7ec38273bb | ||
|
|
6bca9e8dff | ||
|
|
6c6d2ac973 | ||
|
|
8747860b95 | ||
|
|
5a00a6b32f | ||
|
|
8a3be4634e | ||
|
|
c075122492 | ||
|
|
593a0c39ff | ||
|
|
823239010a | ||
|
|
b8dc1a7b16 | ||
|
|
710c1ec457 | ||
|
|
b9247c16df | ||
|
|
59af28af86 | ||
|
|
0baf0fec99 | ||
|
|
d14a0e0c4a | ||
|
|
e7c1782c51 | ||
|
|
7b02f90f09 | ||
|
|
4ed6156a03 | ||
|
|
a0c7670738 | ||
|
|
00ec580e74 | ||
|
|
3442dab121 | ||
|
|
2e747fe2a5 | ||
|
|
df2537b721 | ||
|
|
bf04ac99a3 | ||
|
|
77c8ae525a | ||
|
|
f7dcffc7be | ||
|
|
a17f3f6b2f | ||
|
|
8576e16b61 | ||
|
|
4949de94c4 | ||
|
|
44e504c604 | ||
|
|
0370871867 | ||
|
|
d3c197ddfc | ||
|
|
8fc2003414 | ||
|
|
c446640591 | ||
|
|
6a97772e79 | ||
|
|
d4bbcc0cfb | ||
|
|
994f6ac101 | ||
|
|
fbf5a286ab | ||
|
|
81e68f241c | ||
|
|
aff886341f | ||
|
|
44530ff366 | ||
|
|
e2baa6401a | ||
|
|
560c23797a | ||
|
|
efee23eca7 | ||
|
|
234ac706a3 | ||
|
|
11e2bf1701 | ||
|
|
c8b73b20c6 | ||
|
|
2921a406d6 | ||
|
|
944cb0dfd1 | ||
|
|
2437dd0158 | ||
|
|
a7660df66e | ||
|
|
e2d5a2dff0 | ||
|
|
9899c7028e | ||
|
|
e1552503ac | ||
|
|
35f4ca76b6 | ||
|
|
e828a4888d | ||
|
|
af7663bd61 | ||
|
|
4d38aaad3b | ||
|
|
89f7f3cf98 | ||
|
|
7a45ac029e | ||
|
|
6d4702484d | ||
|
|
a50641d973 | ||
|
|
3b798d1e64 | ||
|
|
553ca77a07 | ||
|
|
e3c46e3b09 | ||
|
|
86657b8742 | ||
|
|
39b69945cc | ||
|
|
15ce0d8e04 | ||
|
|
8bd26974b7 | ||
|
|
38097d47eb | ||
|
|
cb5813d10a | ||
|
|
2cc1207dc3 | ||
|
|
6f12336f33 | ||
|
|
49841871aa | ||
|
|
1b306447ef | ||
|
|
dfa885f4f0 | ||
|
|
08dc228718 | ||
|
|
cabdcdb898 | ||
|
|
627b17d2f5 | ||
|
|
33bccf2ac4 | ||
|
|
9919fd1c43 | ||
|
|
b955c79fb9 | ||
|
|
1fdb8da844 | ||
|
|
a43bbd4f44 | ||
|
|
cbe21e28ec | ||
|
|
aa8adb4a36 | ||
|
|
7005ba3d88 | ||
|
|
276199aa37 | ||
|
|
ee656ff4bb | ||
|
|
c6ac96b64e | ||
|
|
2e70663bf9 | ||
|
|
80cb2445fc | ||
|
|
c3c182e674 | ||
|
|
fb66c7e5ee | ||
|
|
8f46e579f4 | ||
|
|
c6c0a6f1fe | ||
|
|
9f08baaa5c | ||
|
|
7bbc556e6e | ||
|
|
c6118d511d | ||
|
|
d242aaf10e | ||
|
|
113b5c2d20 | ||
|
|
b97e5e2a8b | ||
|
|
54a8025903 | ||
|
|
02d4c5704c | ||
|
|
e0791a61a2 | ||
|
|
5002488c24 | ||
|
|
20f19cbafd | ||
|
|
86d532630f | ||
|
|
fcd9f49e24 | ||
|
|
255292cc01 | ||
|
|
9b9ab2349a | ||
|
|
122df6f233 | ||
|
|
274cb33e89 | ||
|
|
ed816ba448 | ||
|
|
ca01158e1c | ||
|
|
60fb624a40 | ||
|
|
58c6540cdf | ||
|
|
79702eb956 | ||
|
|
fed0d4f65b | ||
|
|
79801a2389 | ||
|
|
c753f3b509 | ||
|
|
9187715bf0 | ||
|
|
85f22aff35 | ||
|
|
4c2acb419b | ||
|
|
5cfe40a504 | ||
|
|
107b14d6bb | ||
|
|
717abe60e7 | ||
|
|
0ccdc3fcfe | ||
|
|
2c9acf141c | ||
|
|
4abfa103b6 | ||
|
|
0db00b9669 | ||
|
|
7594e11f23 | ||
|
|
d58af9ad64 | ||
|
|
3b59591f14 | ||
|
|
53e917bb08 | ||
|
|
9deb181814 | ||
|
|
37a9ed4b3b | ||
|
|
81ec7190d6 | ||
|
|
4667ffb091 | ||
|
|
3face791c6 | ||
|
|
3de028af98 | ||
|
|
24a24306ae | ||
|
|
dcb821d56b | ||
|
|
2d4841b764 | ||
|
|
d83d89f927 | ||
|
|
45e6dadac8 | ||
|
|
be6840b9c2 | ||
|
|
8a276c8595 | ||
|
|
8aa5ac4172 | ||
|
|
eb081e5dfa | ||
|
|
36b793408b | ||
|
|
6822561fd1 | ||
|
|
cb18e6f08f | ||
|
|
5f32d8f5ea | ||
|
|
c2f14bb2da | ||
|
|
dddfcf2832 | ||
|
|
c5c8b7d20a | ||
|
|
c726aa30df | ||
|
|
42317a359d | ||
|
|
8a43919e51 | ||
|
|
7753df7681 | ||
|
|
5603887437 | ||
|
|
d307ac54f6 | ||
|
|
8589701bea | ||
|
|
b2e0b720ac | ||
|
|
9d196a2022 | ||
|
|
1ea4934758 | ||
|
|
b2427b1742 | ||
|
|
b41d564c01 | ||
|
|
d9fe0cc0d4 | ||
|
|
19f8ec9654 | ||
|
|
38070099d8 | ||
|
|
c3bc7b124b | ||
|
|
c0fb7f3af0 | ||
|
|
4fa76da7f8 | ||
|
|
a614374641 | ||
|
|
8f92f3b114 | ||
|
|
7d7b4f2afc | ||
|
|
ba0b55101c | ||
|
|
95d063dbd1 | ||
|
|
047755b702 | ||
|
|
654737a396 | ||
|
|
8615cbf286 | ||
|
|
154a42bd69 | ||
|
|
e6291d57f9 | ||
|
|
e9c9b8391d | ||
|
|
3ab7f6dfb8 | ||
|
|
a3267d1b3a | ||
|
|
8dbaa7ea33 | ||
|
|
8939af4f97 | ||
|
|
6bcce7e562 | ||
|
|
6af58b6c4a | ||
|
|
0c594a5cc5 | ||
|
|
c44f4d78b1 | ||
|
|
9face881c7 | ||
|
|
f60f4dd975 | ||
|
|
3c40c84cde | ||
|
|
7b93ed069f | ||
|
|
3b508242ba | ||
|
|
8d240b84bb | ||
|
|
528267b4ba | ||
|
|
bf18493b9a | ||
|
|
17856d1067 | ||
|
|
e330bbdad2 | ||
|
|
31a0806e2b | ||
|
|
e0c41e4feb | ||
|
|
299013f4f7 | ||
|
|
79adc251a6 | ||
|
|
99172ecdad | ||
|
|
8e31d114f8 | ||
|
|
90b6d52969 | ||
|
|
240c26edf8 | ||
|
|
de1be5e574 | ||
|
|
3a09552f97 | ||
|
|
edd4464176 | ||
|
|
3aecfc8b54 | ||
|
|
4958b227d7 | ||
|
|
7c84589565 | ||
|
|
0d89e7120f | ||
|
|
94df5237a6 | ||
|
|
62bf5171e4 | ||
|
|
b7900844d2 | ||
|
|
52707276e6 | ||
|
|
0ac2b50df1 | ||
|
|
f2c04dff35 | ||
|
|
c8fed2d977 | ||
|
|
ce051cd1a1 | ||
|
|
12ab87c248 | ||
|
|
10a9cf5687 | ||
|
|
38cfeae83b | ||
|
|
555fee7d72 | ||
|
|
7b2ad5b30e | ||
|
|
fb85efc58d | ||
|
|
47eb770257 | ||
|
|
3e8abe7a38 | ||
|
|
9c71a03e56 | ||
|
|
1f59ebb225 | ||
|
|
221644a271 | ||
|
|
1ceb07e7f0 | ||
|
|
6dc994c19c | ||
|
|
5ff464fc96 | ||
|
|
28cf0432f3 | ||
|
|
6886790a18 | ||
|
|
71012e03d6 | ||
|
|
79bb7135db | ||
|
|
1aeafe53d8 | ||
|
|
7880173c63 | ||
|
|
55a2f02431 | ||
|
|
c798928369 | ||
|
|
9ab182913c | ||
|
|
99b8903808 | ||
|
|
57fc8a7b3d | ||
|
|
630de255d8 | ||
|
|
cc14818ffa | ||
|
|
e1db77a27a | ||
|
|
c6a74cdb98 | ||
|
|
c68386e0e2 | ||
|
|
99a7cd31b4 | ||
|
|
239c95771f | ||
|
|
a3d9d9382f | ||
|
|
d8e71510f7 | ||
|
|
cd776a1a96 | ||
|
|
afa90259dd | ||
|
|
9930d6c2ea | ||
|
|
d8f1e91471 | ||
|
|
6ec314e229 | ||
|
|
79dd874576 | ||
|
|
656f1445cb | ||
|
|
540c874ac0 | ||
|
|
84a0453a9b | ||
|
|
88607c404c | ||
|
|
5ed88ccd75 | ||
|
|
7cba69c5f7 | ||
|
|
f2a2e421b5 | ||
|
|
c4d6b80c18 | ||
|
|
4fc094c17f | ||
|
|
78335d324d | ||
|
|
134fc6aad9 | ||
|
|
d9a3b77753 | ||
|
|
d3d48f0e38 | ||
|
|
8bf728103b | ||
|
|
a9eb521865 | ||
|
|
e8a5cbb561 | ||
|
|
3a8c201fbe | ||
|
|
93f8b01d32 | ||
|
|
c55e82300c | ||
|
|
85d8a2d021 | ||
|
|
d372fe7eb6 | ||
|
|
a8e5a3069f | ||
|
|
bdb78e3331 | ||
|
|
75b1c683e0 | ||
|
|
b6baf62508 | ||
|
|
7ebb52d297 | ||
|
|
458b26712c | ||
|
|
b0e5d9894a | ||
|
|
50ed70f29b | ||
|
|
c251c3936d | ||
|
|
69f46f9664 | ||
|
|
0bbbad9add | ||
|
|
9b4b96d3a4 | ||
|
|
990f85cb5b | ||
|
|
584a3696a1 | ||
|
|
c5c10dd086 | ||
|
|
e531f6a36b | ||
|
|
9e3a4a3bbe | ||
|
|
486e9fb7d0 | ||
|
|
d1597500a3 | ||
|
|
4ae6580c4d | ||
|
|
7022c5ab4c | ||
|
|
31365bddf2 | ||
|
|
e2ebbd2f6e | ||
|
|
766f3015aa | ||
|
|
c9ee2a34e4 | ||
|
|
fd1eb89c1e | ||
|
|
4eabbf4661 | ||
|
|
f2386afe6c | ||
|
|
e65cf12625 | ||
|
|
addf6b936f | ||
|
|
4d4895435a | ||
|
|
c20ebd66e4 | ||
|
|
17bc13a877 | ||
|
|
04c031fc40 | ||
|
|
90bfc0d610 | ||
|
|
dce2e464da | ||
|
|
0077c223c8 | ||
|
|
47b202f7bc | ||
|
|
68c546add2 | ||
|
|
2fb8281127 | ||
|
|
fc416722a6 | ||
|
|
0d4c9eda69 | ||
|
|
304d6adc25 | ||
|
|
3dabcb0fa7 | ||
|
|
b3ce4ff613 | ||
|
|
c94a86b7f6 | ||
|
|
4b34085c5f | ||
|
|
5c7db73fd0 | ||
|
|
98c02be4cd | ||
|
|
3c27207c8e | ||
|
|
8c850ca22b | ||
|
|
ddbadcb64d | ||
|
|
29561e2395 | ||
|
|
0ba14de20a | ||
|
|
2cd1377229 | ||
|
|
edd5374247 | ||
|
|
ecf185ee61 | ||
|
|
176422becc | ||
|
|
d2512412d6 | ||
|
|
359e765322 | ||
|
|
cbc8f54ed7 | ||
|
|
99401dcc4f | ||
|
|
39bef7f604 | ||
|
|
457813a231 | ||
|
|
fef878d1f3 | ||
|
|
e866c2a651 | ||
|
|
e79a646b4e | ||
|
|
81e9416d13 | ||
|
|
40a14569ab | ||
|
|
3f754a806e | ||
|
|
5557170a95 | ||
|
|
e3beb61a5f | ||
|
|
9a42c63542 | ||
|
|
731dea9a16 | ||
|
|
4677bf077e | ||
|
|
15ae487adc | ||
|
|
dd248d7dc2 | ||
|
|
888f253ac5 | ||
|
|
ecb46c34ce | ||
|
|
7049b1ce99 | ||
|
|
80a07aee73 | ||
|
|
a7d8829eaa | ||
|
|
eb1573a1fe | ||
|
|
1a75b7d669 | ||
|
|
c0324f00c3 | ||
|
|
41a8e21fe8 | ||
|
|
ef3e10ccb4 | ||
|
|
c36a42be8c | ||
|
|
d3226aa818 | ||
|
|
7cbdf27d93 | ||
|
|
c3c3cf98cb | ||
|
|
813e601bc1 | ||
|
|
0ed5c2683c | ||
|
|
4139271563 | ||
|
|
48a3624644 | ||
|
|
fcc6535910 | ||
|
|
cb21e9b167 | ||
|
|
d1a640f383 | ||
|
|
f838297f05 | ||
|
|
1ea4c09c80 | ||
|
|
5ed1521c6a | ||
|
|
1d06af72da | ||
|
|
313bfc0b72 | ||
|
|
096350920c | ||
|
|
ff460c1f43 | ||
|
|
31fdca5373 | ||
|
|
4f42a0e730 | ||
|
|
833aa02d92 | ||
|
|
61be103f7f | ||
|
|
23d6736c12 | ||
|
|
d8632d8554 | ||
|
|
417900da5b | ||
|
|
6bbd85cc37 | ||
|
|
a3660ecede | ||
|
|
fe91c90324 | ||
|
|
a24a153289 | ||
|
|
baffd6a778 | ||
|
|
74289f0e6d | ||
|
|
a0135f2b71 | ||
|
|
695c8f2a49 | ||
|
|
426e6411d1 | ||
|
|
c9a27f1ad8 | ||
|
|
57dfd608d3 | ||
|
|
4fc19f3234 | ||
|
|
2232081465 | ||
|
|
ed2830ef50 | ||
|
|
beea9fae4b | ||
|
|
05b4a078a7 | ||
|
|
658a50cbcd | ||
|
|
b198267022 | ||
|
|
f41a7619b2 | ||
|
|
19c31d8150 | ||
|
|
8686fdbb7e | ||
|
|
583494274e | ||
|
|
83049d8a71 | ||
|
|
46fc5f3d39 | ||
|
|
7fccf1b2cd | ||
|
|
b470de0882 | ||
|
|
3e5e6bcd75 | ||
|
|
7c626d449f | ||
|
|
b4d8cc5230 | ||
|
|
9b6d6fe6f5 | ||
|
|
377a6939d1 | ||
|
|
2cc56f0b82 | ||
|
|
e42e1b9c51 | ||
|
|
c560b6e2fc | ||
|
|
9a805dffd4 | ||
|
|
cd660f8f93 | ||
|
|
0c27b25a83 | ||
|
|
24001f7b26 | ||
|
|
32042580ba | ||
|
|
36155d9ba0 | ||
|
|
de2f06a21c | ||
|
|
d8567a6171 | ||
|
|
fb75dd8e4d | ||
|
|
9686afcf6f | ||
|
|
c6245b2455 | ||
|
|
fd8d7ac7ed | ||
|
|
6e0aff86ed | ||
|
|
c751613c57 | ||
|
|
50ef788efd | ||
|
|
e7719de38f | ||
|
|
9c83eaa58b | ||
|
|
d6f1b3bde2 | ||
|
|
65d4e45c5e | ||
|
|
f1445d7fb2 | ||
|
|
82ecfd499a | ||
|
|
aa3b036630 | ||
|
|
8aaf0927d2 | ||
|
|
44346ba200 | ||
|
|
43f75a28b0 | ||
|
|
bd34ba8077 | ||
|
|
18a91f783e | ||
|
|
b0e680dfe8 | ||
|
|
212cd0289a | ||
|
|
6faf2b7b0e | ||
|
|
b0c572a9fe | ||
|
|
09d6c9f1a1 | ||
|
|
08534241df | ||
|
|
0a0d4a015e | ||
|
|
d7de30ef8a | ||
|
|
16ef5d63ba | ||
|
|
118b26f0a2 | ||
|
|
293cd25271 | ||
|
|
121c23e8f1 | ||
|
|
11d7179051 | ||
|
|
33b152c18f | ||
|
|
e553c0062b | ||
|
|
d032b463e5 | ||
|
|
c8f2f162ca | ||
|
|
34fc0e801a | ||
|
|
5e7ae549f3 | ||
|
|
a503c0708c | ||
|
|
e38f5be83c | ||
|
|
394b6622f9 | ||
|
|
aee43fea12 | ||
|
|
f4bb30b9f4 | ||
|
|
366346e2a9 | ||
|
|
ef65832291 | ||
|
|
a511565933 | ||
|
|
80a64ac0a4 | ||
|
|
af4ac70ceb | ||
|
|
bcd5fa4e58 | ||
|
|
de81226f53 | ||
|
|
25178fd616 | ||
|
|
5bc5219fa2 | ||
|
|
275644aa02 | ||
|
|
1ab64b2775 | ||
|
|
453e035885 | ||
|
|
16247bb8c3 | ||
|
|
2376e1ea18 | ||
|
|
5e6073f46e | ||
|
|
9f9151580d | ||
|
|
abce07cda6 | ||
|
|
f5747f6a5c | ||
|
|
b3d6b681cb | ||
|
|
d3f02b48cf | ||
|
|
2ae37ecfa5 | ||
|
|
f6e1be3cc0 | ||
|
|
b0bde1341c | ||
|
|
0b23cf591c | ||
|
|
93e2d43c4f | ||
|
|
6448a627f6 | ||
|
|
67fe62b449 | ||
|
|
4911fba3e0 | ||
|
|
8e3748cd8d | ||
|
|
54c0f43f19 | ||
|
|
3d66f42043 | ||
|
|
1962354f5f | ||
|
|
29c0385b12 | ||
|
|
a46012033f | ||
|
|
ecc493ac96 | ||
|
|
b1cf8d3dc1 | ||
|
|
63d52eba72 | ||
|
|
f2ede78953 | ||
|
|
2cfa980930 | ||
|
|
36ab0b9862 | ||
|
|
e8389a46b3 | ||
|
|
f2d6f998ab | ||
|
|
e537012881 | ||
|
|
590b0bea31 | ||
|
|
b0330ce1d8 | ||
|
|
bd7b7c18c3 | ||
|
|
5230300d30 | ||
|
|
fa3c55840c | ||
|
|
eb66a35128 | ||
|
|
3484bb3cf6 | ||
|
|
710956aed5 | ||
|
|
66b81709e7 | ||
|
|
66de644f33 | ||
|
|
5aec4fae19 | ||
|
|
5cd720a388 | ||
|
|
5291db9ad4 | ||
|
|
414a7cd087 | ||
|
|
5385a8b0dc | ||
|
|
4aec4f9d7a | ||
|
|
ff5eb9d96a | ||
|
|
34a0655774 | ||
|
|
72fbb7ed15 | ||
|
|
efb0afd0d9 | ||
|
|
e84a08c4f2 | ||
|
|
d816cd15ad | ||
|
|
48bf98a77b | ||
|
|
a37cf1b88c | ||
|
|
f3be1fe1f5 | ||
|
|
4a114902d6 | ||
|
|
3e0c0c6ef6 | ||
|
|
20d632465b | ||
|
|
f1b622ebfd | ||
|
|
28cfc39b9c | ||
|
|
fc5d514ec1 | ||
|
|
8889a6c8c3 | ||
|
|
45cdd756eb | ||
|
|
a119a30fa5 | ||
|
|
157923420f | ||
|
|
48badeb0a0 | ||
|
|
3f0b68c21d | ||
|
|
6c60ad459c | ||
|
|
f4e95e1215 | ||
|
|
aee6aa8fe6 | ||
|
|
3f41302794 | ||
|
|
6fd3097bd9 | ||
|
|
df94a6e1fb | ||
|
|
d8be8f15a9 | ||
|
|
c5ac987475 | ||
|
|
d8858e5e3d | ||
|
|
c7c5731104 | ||
|
|
65a1e69462 | ||
|
|
e2158ef4fd | ||
|
|
5b03c5d974 | ||
|
|
442f829ef5 | ||
|
|
c4028b0016 | ||
|
|
01ff5ca004 | ||
|
|
bdab0474c4 | ||
|
|
46be5458ef | ||
|
|
97d8e43b49 | ||
|
|
bf3100e68f | ||
|
|
57e97c6375 | ||
|
|
5c66e377f8 | ||
|
|
a63448d447 | ||
|
|
4fdbb1ee33 | ||
|
|
9fd3717645 | ||
|
|
8437b4fd1f | ||
|
|
3d9982357b | ||
|
|
b0626d8de8 | ||
|
|
e7fffbae3a | ||
|
|
8a68bf2108 | ||
|
|
7052c162e3 | ||
|
|
73161f3758 | ||
|
|
549d13b717 | ||
|
|
dcb2cfe246 | ||
|
|
e4e10ead0f | ||
|
|
4e70ce8578 | ||
|
|
8a2846741b | ||
|
|
18f3c52ba8 | ||
|
|
d894237ab5 | ||
|
|
a4de63b13f | ||
|
|
66e3bfcb6f | ||
|
|
efef4590b6 | ||
|
|
36b9f63707 | ||
|
|
0c6490c510 | ||
|
|
accd03a8e9 | ||
|
|
8e0137347b | ||
|
|
1fdc0dd63d | ||
|
|
7aab504ecb | ||
|
|
6be8a72e81 | ||
|
|
a0f18930f3 | ||
|
|
3c9695f9c1 | ||
|
|
4e6a59ebc5 | ||
|
|
26a6004ee5 | ||
|
|
6ce541c063 | ||
|
|
375512ec17 | ||
|
|
6076ac618a | ||
|
|
85c9b33621 | ||
|
|
dede6be4d3 | ||
|
|
3ed9e56dc3 | ||
|
|
507ae54044 | ||
|
|
1ef669b5e7 | ||
|
|
c0d6cc88d7 | ||
|
|
69d34f7607 | ||
|
|
904fb90f5b | ||
|
|
308f8c6f5a | ||
|
|
18bd64b147 | ||
|
|
7b68db9dc6 | ||
|
|
7c9ceaa4dc | ||
|
|
2ccf861503 | ||
|
|
1c4d30c374 | ||
|
|
1af242e154 | ||
|
|
9fa5ff18a7 | ||
|
|
2044998a27 | ||
|
|
a8e690e5f3 | ||
|
|
0cabecc05f | ||
|
|
51c1f16947 | ||
|
|
3e04648a41 | ||
|
|
280432204f | ||
|
|
6e5aeba187 | ||
|
|
1089796d49 | ||
|
|
7d4df6e369 | ||
|
|
45b0402226 | ||
|
|
bebc4f646c | ||
|
|
c9555eb0f5 | ||
|
|
33bfa4c9dc | ||
|
|
a6368a8091 | ||
|
|
55712f7bdf | ||
|
|
316b45f11a | ||
|
|
2fceed22ac | ||
|
|
5fa1b6024a | ||
|
|
c50cfe14e0 | ||
|
|
34fc4eee76 | ||
|
|
3bd6083e68 | ||
|
|
a6acd08bd0 | ||
|
|
6274d552d6 |
22
.devcontainer/devcontainer.json
Normal file
22
.devcontainer/devcontainer.json
Normal file
@@ -0,0 +1,22 @@
|
||||
// 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"
|
||||
]
|
||||
}
|
||||
11
.docker/docker-entrypoint.sh
Executable file
11
.docker/docker-entrypoint.sh
Executable file
@@ -0,0 +1,11 @@
|
||||
#!/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;"
|
||||
29
.editorconfig
Normal file
29
.editorconfig
Normal file
@@ -0,0 +1,29 @@
|
||||
# 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
|
||||
5
.gitattributes
vendored
Normal file
5
.gitattributes
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
* text=auto eol=lf
|
||||
|
||||
*.sh text eol=lf
|
||||
|
||||
* linguist-language=go
|
||||
39
.github/CODEOWNERS
vendored
Normal file
39
.github/CODEOWNERS
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
* @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
|
||||
13
.github/FUNDING.yml
vendored
Normal file
13
.github/FUNDING.yml
vendored
Normal file
@@ -0,0 +1,13 @@
|
||||
# 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']
|
||||
39
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
Normal file
39
.github/ISSUE_TEMPLATE/Bug_report.md
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
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-->
|
||||
|
||||
|
||||
<!--提交问题之前请点击预览标签,符合要求之后再提交问题-->
|
||||
39
.github/ISSUE_TEMPLATE/Custom.md
vendored
Normal file
39
.github/ISSUE_TEMPLATE/Custom.md
vendored
Normal file
@@ -0,0 +1,39 @@
|
||||
---
|
||||
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-->
|
||||
|
||||
|
||||
<!--提交问题之前请点击预览标签,符合要求之后再提交问题-->
|
||||
5
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
Normal file
5
.github/ISSUE_TEMPLATE/Feature_request.md
vendored
Normal file
@@ -0,0 +1,5 @@
|
||||
---
|
||||
name: Feature request
|
||||
about: Suggest an idea for docker_practice
|
||||
|
||||
---
|
||||
20
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
20
.github/PULL_REQUEST_TEMPLATE.md
vendored
Normal file
@@ -0,0 +1,20 @@
|
||||
<!--
|
||||
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
|
||||
-->
|
||||
27
.github/dependabot.yml
vendored
Normal file
27
.github/dependabot.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
version: 2
|
||||
updates:
|
||||
- package-ecosystem: "npm"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
prefix: "chore(deps)"
|
||||
labels:
|
||||
- "dependencies"
|
||||
groups:
|
||||
dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
|
||||
- package-ecosystem: "github-actions"
|
||||
directory: "/"
|
||||
schedule:
|
||||
interval: "weekly"
|
||||
commit-message:
|
||||
prefix: "chore(deps)"
|
||||
labels:
|
||||
- "dependencies"
|
||||
groups:
|
||||
dependencies:
|
||||
patterns:
|
||||
- "*"
|
||||
65
.github/workflows/check-link.yml
vendored
Normal file
65
.github/workflows/check-link.yml
vendored
Normal file
@@ -0,0 +1,65 @@
|
||||
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
|
||||
154
.github/workflows/ci.yaml
vendored
Normal file
154
.github/workflows/ci.yaml
vendored
Normal file
@@ -0,0 +1,154 @@
|
||||
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
|
||||
- name: Build Gitbook
|
||||
uses: docker://yeasy/docker_practice
|
||||
with:
|
||||
args: 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
|
||||
npm i
|
||||
|
||||
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}}
|
||||
27
.github/workflows/dependabot-automerge.yml
vendored
Normal file
27
.github/workflows/dependabot-automerge.yml
vendored
Normal file
@@ -0,0 +1,27 @@
|
||||
name: Dependabot auto-merge
|
||||
on: pull_request
|
||||
|
||||
permissions:
|
||||
contents: write
|
||||
pull-requests: write
|
||||
|
||||
jobs:
|
||||
dependabot:
|
||||
runs-on: ubuntu-latest
|
||||
if: github.actor == 'dependabot[bot]'
|
||||
steps:
|
||||
- name: Dependabot metadata
|
||||
id: metadata
|
||||
uses: dependabot/fetch-metadata@v2
|
||||
with:
|
||||
github-token: "${{ secrets.GITHUB_TOKEN }}"
|
||||
- name: Approve a PR
|
||||
run: gh pr review --approve "$PR_URL"
|
||||
env:
|
||||
PR_URL: ${{github.event.pull_request.html_url}}
|
||||
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
- name: Enable auto-merge for Dependabot PRs
|
||||
run: gh pr merge --auto --merge "$PR_URL"
|
||||
env:
|
||||
PR_URL: ${{github.event.pull_request.html_url}}
|
||||
GH_TOKEN: ${{secrets.GITHUB_TOKEN}}
|
||||
19
.gitignore
vendored
19
.gitignore
vendored
@@ -3,6 +3,25 @@
|
||||
*.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
|
||||
|
||||
3
.vuepress/.gitignore
vendored
Normal file
3
.vuepress/.gitignore
vendored
Normal file
@@ -0,0 +1,3 @@
|
||||
/*
|
||||
!.gitignore
|
||||
!config.js
|
||||
463
.vuepress/config.js
Normal file
463
.vuepress/config.js
Normal file
@@ -0,0 +1,463 @@
|
||||
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',
|
||||
],
|
||||
},
|
||||
}
|
||||
});
|
||||
3
.zhlintignore
Normal file
3
.zhlintignore
Normal file
@@ -0,0 +1,3 @@
|
||||
node_modules/
|
||||
.vuepress/
|
||||
.git/
|
||||
68
01_introduction/1.1_quickstart.md
Normal file
68
01_introduction/1.1_quickstart.md
Normal file
@@ -0,0 +1,68 @@
|
||||
## 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)做深入了解。
|
||||
120
01_introduction/1.2_what.md
Normal file
120
01_introduction/1.2_what.md
Normal file
@@ -0,0 +1,120 @@
|
||||
## 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 最大的贡献不仅是技术本身,更是它 **让容器技术从系统管理员的工具变成了每个开发者都能使用的标准工具**。
|
||||
206
01_introduction/1.3_why.md
Normal file
206
01_introduction/1.3_why.md
Normal file
@@ -0,0 +1,206 @@
|
||||
## 1.3 为什么要用 Docker
|
||||
|
||||
在回答 “为什么用 Docker” 之前,笔者想先问一个问题:**你有没有经历过这些场景?**
|
||||
|
||||
### 1.3.1 没有 Docker 的世界
|
||||
|
||||
在 Docker 出现之前,软件开发和运维面临着诸多棘手的问题。我们先来看看以下三个典型的痛点场景。
|
||||
|
||||
#### 场景一:“在我电脑上明明能跑”
|
||||
|
||||
```bash
|
||||
周五下午 5:00
|
||||
├── 开发者:代码写完了,本地测试通过,提交!🎉
|
||||
├── 周一早上 9:00
|
||||
│ └── 测试:"这个功能在测试环境跑不起来"
|
||||
└── 开发者:" 不可能,在我电脑上明明能跑啊……"
|
||||
```
|
||||
|
||||
笔者统计过,这个问题通常由以下原因导致:
|
||||
|
||||
- Python/Node/Java 版本不一致
|
||||
- 依赖库版本不一致
|
||||
- 操作系统配置不一致
|
||||
- 某些环境变量没有设置
|
||||
- “哦,忘了说我本地装了个 XXX”
|
||||
|
||||
#### 场景二:环境配置的噩梦
|
||||
|
||||
```bash
|
||||
新同事入职
|
||||
├── Day 1:领电脑,配环境
|
||||
├── Day 2:继续配环境,遇到问题
|
||||
├── Day 3:换种方法配环境
|
||||
├── Day 4:问老同事怎么配的,他也忘了
|
||||
└── Day 5:终于能跑起来了!但不知道为什么……
|
||||
```
|
||||
|
||||
#### 场景三:服务器迁移的恐惧
|
||||
|
||||
```bash
|
||||
运维:"我们需要把服务迁移到新服务器"
|
||||
开发:"旧服务器上的配置文档在哪?"
|
||||
运维:"当时是一个已经离职的同事配的……"
|
||||
所有人:😱
|
||||
```
|
||||
|
||||
### 1.3.2 Docker 如何解决这些问题
|
||||
|
||||
Docker 的出现为上述问题提供了完美的解决方案。它通过 “一次构建,到处运行” 的核心理念,从根本上改变了软件交付的方式。
|
||||
|
||||
#### 核心理念:一次构建,到处运行
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
dev["开发团队"] -->|创建| img["Docker 镜像"]
|
||||
img -->|测试团队验证| test["测试团队"]
|
||||
test -- "有问题<br/>反馈修改和更新" --> dev
|
||||
test -- "没问题<br/>发布" --> prod["生产环境"]
|
||||
```
|
||||
|
||||
### 1.3.3 Docker 的核心优势
|
||||
|
||||
除了解决上述痛点,Docker 还拥有诸多显著的技术优势,包括环境一致性、秒级启动、高效的资源利用等。
|
||||
|
||||
#### 1. 环境一致性
|
||||
|
||||
Docker 镜像包含了应用运行所需的 **一切**:代码、运行时、系统工具、库、配置。这意味着:
|
||||
|
||||
- ✅ 开发环境和生产环境完全一致
|
||||
- ✅ 不会再有 “在我机器上能跑” 的问题
|
||||
- ✅ 新人入职,一条命令就能启动开发环境
|
||||
|
||||
```bash
|
||||
## 新同事入职第一天
|
||||
|
||||
$ git clone https://github.com/company/project.git
|
||||
$ docker compose up
|
||||
## 完整的开发环境就准备好了
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
#### 2. 秒级启动
|
||||
|
||||
传统虚拟机启动需要几分钟 (引导操作系统),而 Docker 容器启动通常只需要 **几秒甚至几百毫秒**。
|
||||
|
||||
笔者实测数据:
|
||||
|
||||
| 启动内容 | 虚拟机 | Docker 容器 |
|
||||
|---------|--------|-------------|
|
||||
| 空系统 | ~60 秒 | ~0.5 秒 |
|
||||
| MySQL | ~90 秒 | ~3 秒 |
|
||||
| 完整 Web 应用 | ~120 秒 | ~5 秒 |
|
||||
|
||||
这个差异对以下场景尤为重要:
|
||||
|
||||
- **CI/CD 流水线**:每次构建节省几分钟,一天累积下来就是几小时
|
||||
- **弹性扩容**:流量高峰时能快速启动更多实例
|
||||
- **开发体验**:快速重启服务进行调试
|
||||
|
||||
#### 3. 资源效率
|
||||
|
||||
Docker 容器共享宿主机内核,无需为每个应用运行完整的操作系统。以一台 64GB 内存的物理服务器为例:
|
||||
- **传统虚拟机方案**:每个虚拟机都需要运行完整的操作系统(每个额外占用如 2GB 内存),产生大量资源开销,实际可用于应用的内存可能只有约 18GB。
|
||||
- **Docker 方案**:容器直接共享宿主机系统,只需付出很少的基础开销(OS 及引擎约 4GB),即可将约 60GB 的内存全部用于实际应用。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph VM ["传统虚拟机方案 ❌"]
|
||||
direction TB
|
||||
Server1["物理服务器 (64GB 内存)"]
|
||||
subgraph VMs ["可用应用内存: 约 18GB"]
|
||||
direction LR
|
||||
VM1["VM 1: 应用 1<br/>(含 2GB OS)"]
|
||||
VM2["VM 2: 应用 2<br/>(含 2GB OS)"]
|
||||
VM3["VM 3: 应用 3<br/>(含 2GB OS)"]
|
||||
end
|
||||
Server1 --- VMs
|
||||
end
|
||||
|
||||
subgraph Docker ["Docker 方案 ✅"]
|
||||
direction TB
|
||||
Server2["物理服务器 (64GB 内存)<br/>含约 4GB OS及引擎配置"]
|
||||
subgraph Containers ["可用应用内存: 约 60GB"]
|
||||
direction LR
|
||||
C1["容器 1: 应用 1<br/>(按需分配)"]
|
||||
C2["容器 2: 应用 2<br/>(按需分配)"]
|
||||
C3["容器 3: 应用 3<br/>(按需分配)"]
|
||||
end
|
||||
Server2 --- Containers
|
||||
end
|
||||
```
|
||||
|
||||
#### 4. 持续交付和部署
|
||||
|
||||
Docker 完美契合 DevOps 的工作流程:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
A["代码提交<br/>(Git push)"] --> B["自动构建镜像<br/>(docker build)"]
|
||||
B --> C["自动测试<br/>(容器内运行测试)"]
|
||||
C --> D["自动部署<br/>(容器滚动更新)"]
|
||||
```
|
||||
|
||||
使用 [Dockerfile](../04_image/4.5_build.md) 定义镜像构建过程,使得:
|
||||
|
||||
- 构建过程 **可重复、可追溯**
|
||||
- 任何人都能从代码重建完全相同的镜像
|
||||
- 配合 [GitHub Actions](../21_case_devops/21.2_github_actions.md) 等 CI 系统实现自动化
|
||||
|
||||
#### 5. 轻松迁移
|
||||
|
||||
Docker 可以在几乎任何平台上运行:
|
||||
|
||||
- ✅ 本地开发机 (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、开发环境;虚拟机适合多租户、高安全需求场景。
|
||||
29
01_introduction/README.md
Normal file
29
01_introduction/README.md
Normal file
@@ -0,0 +1,29 @@
|
||||
# 第一章 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. 建立起对容器技术的初步认知,为后续的实战操作打下基础。
|
||||
|
||||
好吧,让我们带着问题开始这神奇之旅。
|
||||
11
01_introduction/summary.md
Normal file
11
01_introduction/summary.md
Normal file
@@ -0,0 +1,11 @@
|
||||
## 本章小结
|
||||
|
||||
- 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)。
|
||||
213
02_basic_concept/2.1_image.md
Normal file
213
02_basic_concept/2.1_image.md
Normal file
@@ -0,0 +1,213 @@
|
||||
## 2.1 镜像
|
||||
|
||||
Docker 镜像作为容器运行的基石,其设计理念和实现机制至关重要。本节将深入探讨镜像的本质、与操作系统的关系、内容构成以及核心的分层存储机制。
|
||||
|
||||
### 2.1.1 一句话理解镜像
|
||||
|
||||
> **Docker 镜像是一个只读的模板,包含了运行应用所需的一切:代码、运行时、库、环境变量和配置文件。** 如果用一个类比:**镜像就像是一张光盘或 ISO 文件**。你可以用同一张光盘在不同电脑上安装系统,而光盘本身不会被修改。同样,一个镜像可以创建多个容器,而镜像本身保持不变。
|
||||
|
||||
### 2.1.2 镜像与操作系统的关系
|
||||
|
||||
我们都知道,操作系统分为 **内核** 和 **用户空间**:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph UserSpace ["用户空间"]
|
||||
direction TB
|
||||
App["应用程序、工具、库、配置文件...<br/>(这部分被打包成 Docker 镜像)"]
|
||||
end
|
||||
|
||||
subgraph KernelSpace ["Linux 内核"]
|
||||
direction TB
|
||||
Kernel["容器共享宿主机的内核"]
|
||||
end
|
||||
|
||||
UserSpace --- KernelSpace
|
||||
```
|
||||
|
||||
对于 Linux 而言,内核启动后会挂载 `root` 文件系统来提供用户空间支持。**Docker 镜像** 本质上就是一个 `root` 文件系统。
|
||||
|
||||
例如,官方镜像 `ubuntu:24.04` 包含了一套完整的 Ubuntu 24.04 最小系统的 root 文件系统——但 **不包含 Linux 内核** (因为容器共享宿主机的内核)。
|
||||
|
||||
### 2.1.3 镜像包含什么?
|
||||
|
||||
Docker 镜像是一个特殊的文件系统,包含:
|
||||
|
||||
| 内容类型 | 示例 |
|
||||
|---------|------|
|
||||
| **程序文件** | 应用二进制文件、Python/Node 解释器 |
|
||||
| **库文件** | 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` |
|
||||
239
02_basic_concept/2.2_container.md
Normal file
239
02_basic_concept/2.2_container.md
Normal file
@@ -0,0 +1,239 @@
|
||||
## 2.2 容器
|
||||
|
||||
容器是 Docker 技术的核心,是应用实际运行的载体。本节将从容器的本质、与虚拟机的区别、存储层机制以及生命周期管理等方面,全面解析 Docker 容器。
|
||||
|
||||
### 2.2.1 一句话理解容器
|
||||
|
||||
> **容器是镜像的运行实例。如果把镜像比作程序,那么容器就是进程。** 用面向对象编程的术语来说:**镜像是类 (Class),容器是对象 (Instance)**。
|
||||
|
||||
- 一个镜像可以创建多个容器
|
||||
- 每个容器相互独立,互不影响
|
||||
- 容器可以被创建、启动、停止、删除、暂停
|
||||
|
||||
### 2.2.2 容器的本质
|
||||
|
||||
> 💡 笔者认为,理解这一点是理解 Docker 的关键:**容器的本质是一个特殊的进程**。
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph NormalProcess ["普通进程"]
|
||||
direction TB
|
||||
N1["• 共享系统资源<br/>• 共享网络<br/>• 共享文件系统"]
|
||||
end
|
||||
|
||||
subgraph ContainerProcess ["容器进程 (运行在宿主机内核上)"]
|
||||
direction TB
|
||||
C1["• 独立进程空间<br/>• 独立网络环境<br/>• 独立文件系统<br/>• 独立用户空间"]
|
||||
end
|
||||
```
|
||||
|
||||
这种隔离是通过 Linux 内核的 **Namespace** 技术实现的。具体表现为:
|
||||
- **进程空间**:容器看不到宿主机上的其他进程。
|
||||
- **网络**:容器拥有独立的 IP、端口等网络资源。
|
||||
- **文件系统**:容器拥有独立的 root 目录。
|
||||
- **用户**:容器内的 root 用户不等于宿主机的 root 用户。
|
||||
|
||||
### 2.2.3 容器 vs 虚拟机:核心区别
|
||||
|
||||
很多初学者会混淆容器和虚拟机。笔者用一张图来说明:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
subgraph VM ["虚拟机 (每个 VM 运行完整 OS)"]
|
||||
direction TB
|
||||
subgraph VMApp ["应用层"]
|
||||
VA[App A] & VB[App B]
|
||||
end
|
||||
subgraph VMGuest ["Guest OS (完整系统)"]
|
||||
G1[Guest OS] & G2[Guest OS]
|
||||
end
|
||||
V[Hypervisor]
|
||||
VMH[Host OS]
|
||||
VMHW[Hardware]
|
||||
VMApp --> VMGuest --> V --> VMH --> VMHW
|
||||
end
|
||||
|
||||
subgraph Container ["容器 (所有容器共享宿主机内核)"]
|
||||
direction TB
|
||||
subgraph CApp ["应用层"]
|
||||
CA[App A] & CB[App B]
|
||||
end
|
||||
subgraph CContainer ["隔离层"]
|
||||
CC1[Container 仅应用] & CC2[Container 仅应用]
|
||||
end
|
||||
CE[Docker Engine]
|
||||
CH[Host OS]
|
||||
CHW[Hardware]
|
||||
CApp --> CContainer --> CE --> CH --> CHW
|
||||
end
|
||||
```
|
||||
|
||||
| 特性 | 容器 | 虚拟机 |
|
||||
|------|------|--------|
|
||||
| **隔离级别** | 进程级 (Namespace)| 硬件级 (Hypervisor)|
|
||||
| **启动时间** | 秒级 (甚至毫秒)| 分钟级 |
|
||||
| **资源占用** | MB 级别 | GB 级别 |
|
||||
| **性能损耗** | 几乎为零 | 5-20% |
|
||||
| **内核** | 共享宿主机内核 | 各自独立内核 |
|
||||
|
||||
### 2.2.4 容器的存储层
|
||||
|
||||
理解容器的存储层机制对于数据的持久化和镜像的优化至关重要。本节将介绍容器的可写层以及 Copy-on-Write 机制。
|
||||
|
||||
#### 镜像层 + 容器层
|
||||
|
||||
当容器运行时,Docker 会在镜像的只读层之上创建一个 **可写层** (容器存储层):
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
ContainerLayer["容器存储层(可读写)<br/>容器运行时创建,记录文件变化"]
|
||||
ImageLayerN["镜像第 N 层(只读)"]
|
||||
ImageLayerN1["镜像第 N-1 层(只读)"]
|
||||
Dots["..."]
|
||||
ImageLayer1["镜像第 1 层(只读)<br/>基础镜像层"]
|
||||
|
||||
ContainerLayer --> ImageLayerN --> ImageLayerN1 --> Dots --> ImageLayer1
|
||||
```
|
||||
|
||||
#### Copy-on-Write (写时复制)
|
||||
|
||||
当容器需要修改镜像层中的文件时:
|
||||
|
||||
1. Docker 将该文件 **复制** 到容器存储层
|
||||
2. 在容器层中进行修改
|
||||
3. 原始镜像层保持不变
|
||||
|
||||
```bash
|
||||
读取文件:直接从镜像层读取(共享,高效)
|
||||
修改文件:复制到容器层,然后修改(只有这个容器能看到修改)
|
||||
```
|
||||
|
||||
#### ⚠️ 容器存储层的生命周期
|
||||
|
||||
> **笔者特别强调**:这是新手最容易踩的坑!**容器存储层与容器生命周期绑定。容器删除,数据就没了!**
|
||||
|
||||
```bash
|
||||
## 创建容器,写入数据
|
||||
|
||||
$ docker run -it ubuntu bash
|
||||
root@abc123:/# echo "important data" > /data.txt
|
||||
root@abc123:/# exit
|
||||
|
||||
## 删除容器
|
||||
|
||||
$ docker rm abc123
|
||||
|
||||
## 数据丢了!没有任何办法恢复!
|
||||
|
||||
```
|
||||
|
||||
#### 正确的数据持久化方式
|
||||
|
||||
按照 Docker 最佳实践,容器存储层应该保持 **无状态**。需要持久化的数据应该使用:
|
||||
|
||||
| 方式 | 说明 | 适用场景 |
|
||||
|------|------|---------|
|
||||
| **[数据卷 (Volume) ](../08_data/8.1_volume.md)** | Docker 管理的存储 | 数据库、应用数据 |
|
||||
| **[绑定挂载 (Bind Mount) ](../08_data/8.2_bind-mounts.md)** | 挂载宿主机目录 | 开发时共享代码 |
|
||||
|
||||
```bash
|
||||
## 使用数据卷(推荐)
|
||||
|
||||
$ docker run -v mydata:/var/lib/mysql mysql
|
||||
|
||||
## 使用绑定挂载
|
||||
|
||||
$ docker run -v /host/path:/container/path nginx
|
||||
```
|
||||
|
||||
这些位置的读写 **会跳过容器存储层**,直接写入宿主机,性能更好,也不会随容器删除而丢失。
|
||||
|
||||
### 2.2.5 容器的生命周期
|
||||
|
||||
掌握容器的生命周期对于管理和调试 Docker 应用非常重要。如图 2-1 所示,容器会经历从创建到删除的完整状态流转。
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
direction TB
|
||||
[*] --> Created : docker create
|
||||
Created --> Running : docker start
|
||||
Running --> Stopped : docker stop
|
||||
Running --> Paused : docker pause
|
||||
Paused --> Running : docker unpause
|
||||
|
||||
Created --> Deleted : docker rm
|
||||
Stopped --> Deleted : docker rm
|
||||
Paused --> Deleted : docker rm
|
||||
|
||||
Deleted --> [*]
|
||||
```
|
||||
|
||||
图 2-1 容器生命周期状态流转图
|
||||
|
||||
#### 常用生命周期命令
|
||||
|
||||
```bash
|
||||
## 创建并启动容器(最常用)
|
||||
|
||||
$ docker run nginx
|
||||
|
||||
## 分步操作
|
||||
|
||||
$ docker create nginx # 创建容器(不启动)
|
||||
$ docker start abc123 # 启动容器
|
||||
|
||||
## 停止容器
|
||||
|
||||
$ docker stop abc123 # 优雅停止(发送 SIGTERM,等待后发送 SIGKILL)
|
||||
$ docker kill abc123 # 强制停止(直接发送 SIGKILL)
|
||||
|
||||
## 暂停/恢复(不常用,但有时有用)
|
||||
|
||||
$ docker pause abc123 # 暂停容器内所有进程
|
||||
$ docker unpause abc123 # 恢复
|
||||
|
||||
## 删除容器
|
||||
|
||||
$ docker rm abc123 # 删除已停止的容器
|
||||
$ docker rm -f abc123 # 强制删除运行中的容器
|
||||
```
|
||||
|
||||
### 2.2.6 容器与进程的关系
|
||||
|
||||
> **核心概念**:容器的生命周期 = 主进程 (PID 1) 的生命周期
|
||||
|
||||
```bash
|
||||
## 主进程运行,容器运行
|
||||
|
||||
## 主进程退出,容器停止
|
||||
|
||||
```
|
||||
|
||||
这就是为什么:
|
||||
|
||||
```bash
|
||||
## 这个容器会立即退出(bash 没有输入就退出了)
|
||||
|
||||
$ docker run ubuntu
|
||||
|
||||
## 这个容器会持续运行(nginx 作为守护进程持续运行)
|
||||
|
||||
$ docker run nginx
|
||||
```
|
||||
|
||||
详细解释请参考[后台运行](../05_container/5.2_daemon.md)章节。
|
||||
|
||||
### 2.2.7 容器的隔离性
|
||||
|
||||
Docker 容器通过以下 Namespace 实现隔离:
|
||||
|
||||
| Namespace | 隔离内容 | 效果 |
|
||||
|-----------|---------|------|
|
||||
| **PID** | 进程 ID | 容器内 PID 1 是应用进程,看不到宿主机其他进程 |
|
||||
| **NET** | 网络 | 独立的网络栈、IP 地址、端口 |
|
||||
| **MNT** | 文件系统 | 独立的根目录和挂载点 |
|
||||
| **UTS** | 主机名 | 独立的主机名和域名 |
|
||||
| **IPC** | 进程间通信 | 独立的信号量、消息队列 |
|
||||
| **USER** | 用户 | 独立的用户和组 ID |
|
||||
|
||||
> 想深入了解?请阅读[底层实现 - 命名空间](../12_implementation/12.2_namespace.md)。
|
||||
288
02_basic_concept/2.3_repository.md
Normal file
288
02_basic_concept/2.3_repository.md
Normal file
@@ -0,0 +1,288 @@
|
||||
## 2.3 仓库
|
||||
|
||||
Docker Registry 是镜像分发和管理的核心组件。本节将介绍 Registry 的基本概念、公共和私有服务的选择,以及镜像的安全管理。
|
||||
|
||||
### 2.3.1 一句话理解 Registry
|
||||
|
||||
> **Docker Registry 是存储和分发 Docker 镜像的服务,类似于代码的 GitHub 或包管理的 npm。**
|
||||
|
||||
镜像构建完成后,可以在当前机器上运行。但如果需要在其他服务器上使用这个镜像,就需要一个集中的存储和分发服务——这就是 Docker Registry。
|
||||
|
||||
### 2.3.2 核心概念
|
||||
|
||||
要熟练使用 Docker Registry,首先需要理清它与仓库 (Repository)、标签 (Tag) 之间的关系。
|
||||
|
||||
#### Registry、仓库、标签的关系
|
||||
|
||||
Docker Registry 中可以包含多个 Repository,每个 Repository 可以包含多个 Tag。如图 2-2 所示,它们之间具有清晰的层级关系。
|
||||
|
||||
```mermaid
|
||||
flowchart TB
|
||||
subgraph Registry ["Docker Registry(如 Docker Hub)"]
|
||||
direction TB
|
||||
subgraph RepoNginx ["Repository(仓库): nginx"]
|
||||
direction LR
|
||||
N1(":latest (tag)")
|
||||
N2(":1.25 (tag)")
|
||||
N3(":1.24 (tag)")
|
||||
N4(":alpine (tag)")
|
||||
N5("...")
|
||||
N1 ~~~ N2 ~~~ N3 ~~~ N4 ~~~ N5
|
||||
end
|
||||
subgraph RepoMysql ["Repository(仓库): mysql"]
|
||||
direction LR
|
||||
M1(":latest")
|
||||
M2(":8.0")
|
||||
M3(":5.7")
|
||||
M4("...")
|
||||
M1 ~~~ M2 ~~~ M3 ~~~ M4
|
||||
end
|
||||
RepoNginx ~~~ RepoMysql
|
||||
end
|
||||
```
|
||||
|
||||
图 2-2 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
|
||||
```
|
||||
15
02_basic_concept/README.md
Normal file
15
02_basic_concept/README.md
Normal file
@@ -0,0 +1,15 @@
|
||||
# 第二章 基本概念
|
||||
|
||||
**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)
|
||||
40
02_basic_concept/summary.md
Normal file
40
02_basic_concept/summary.md
Normal file
@@ -0,0 +1,40 @@
|
||||
## 本章小结
|
||||
|
||||
本章介绍了 Docker 的三个核心概念:镜像、容器和仓库。
|
||||
|
||||
| 概念 | 要点 |
|
||||
|------|------|
|
||||
| **镜像是什么** | 只读的应用模板,包含运行所需的一切 |
|
||||
| **分层存储** | 多层叠加,共享基础层,节省空间 |
|
||||
| **只读特性** | 构建后不可修改,保证一致性 |
|
||||
| **层的陷阱** | 删除操作只是标记,不减小体积 |
|
||||
| **容器是什么** | 镜像的运行实例,本质是隔离的进程 |
|
||||
| **容器 vs 虚拟机** | 共享内核,更轻量,但隔离性较弱 |
|
||||
| **存储层** | 可写层随容器删除而消失 |
|
||||
| **数据持久化** | 使用 Volume 或 Bind Mount |
|
||||
| **生命周期** | 与主进程 (PID 1) 绑定 |
|
||||
| **Registry** | 存储和分发镜像的服务 |
|
||||
| **仓库 (Repository)** | 同一软件的镜像集合 |
|
||||
| **标签 (Tag)** | 版本标识,默认为 latest |
|
||||
| **Docker Hub** | 默认的公共 Registry |
|
||||
| **私有 Registry** | 企业内部使用,推荐 Harbor |
|
||||
|
||||
现在你已经了解了 Docker 的三个核心概念:[镜像](2.1_image.md)、[容器](2.2_container.md)和仓库。接下来,让我们开始[安装 Docker](../03_install/README.md),动手实践!
|
||||
|
||||
### 延伸阅读
|
||||
|
||||
- [获取镜像](../04_image/4.1_pull.md):从 Registry 下载镜像
|
||||
- [使用 Dockerfile 定制镜像](../04_image/4.5_build.md):创建自己的镜像
|
||||
- [Dockerfile 最佳实践](../appendix/best_practices.md):构建高质量镜像的技巧
|
||||
- [底层实现 - 联合文件系统](../12_implementation/12.4_ufs.md):深入理解分层存储的技术原理
|
||||
- [启动容器](../05_container/5.1_run.md):详细的容器启动选项
|
||||
- [后台运行](../05_container/5.2_daemon.md):理解容器为什么会“立即退出”
|
||||
- [进入容器](../05_container/5.4_attach_exec.md):如何操作运行中的容器
|
||||
- [数据管理](../08_data/README.md):Volume 和数据持久化详解
|
||||
- [Docker Hub](../06_repository/6.1_dockerhub.md):Docker Hub 的详细使用
|
||||
- [私有仓库](../06_repository/6.2_registry.md):搭建私有 Registry
|
||||
- [私有仓库高级配置](../06_repository/6.3_registry_auth.md):认证、TLS 配置
|
||||
- [镜像加速器](../03_install/3.9_mirror.md):配置镜像加速
|
||||
---
|
||||
|
||||
> 📝 **发现错误或有改进建议?** 欢迎提交 [Issue](https://github.com/yeasy/docker_practice/issues) 或 [PR](https://github.com/yeasy/docker_practice/pulls)。
|
||||
19
03_install/3.10_experimental.md
Normal file
19
03_install/3.10_experimental.md
Normal file
@@ -0,0 +1,19 @@
|
||||
## 3.10 开启实验特性
|
||||
|
||||
一些 docker 命令或功能仅当 **实验特性** 开启时才能使用,请按照以下方法进行设置。
|
||||
|
||||
### 3.10.1 Docker CLI 的实验特性
|
||||
|
||||
CLI 的实验特性通常包含仍在开发中的新功能。幸运的是,在较新版本中这些特性已经更加易用。
|
||||
|
||||
从 `v20.10` 版本开始,Docker CLI 所有实验特性的命令均默认开启,无需再进行配置或设置系统环境变量。
|
||||
|
||||
### 3.10.2 开启 dockerd 的实验特性
|
||||
|
||||
编辑 `/etc/docker/daemon.json`,新增如下条目
|
||||
|
||||
```json
|
||||
{
|
||||
"experimental": true
|
||||
}
|
||||
```
|
||||
187
03_install/3.1_ubuntu.md
Normal file
187
03_install/3.1_ubuntu.md
Normal file
@@ -0,0 +1,187 @@
|
||||
## 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 \
|
||||
|
||||
## $ 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/)
|
||||
178
03_install/3.2_debian.md
Normal file
178
03_install/3.2_debian.md
Normal file
@@ -0,0 +1,178 @@
|
||||
## 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 \
|
||||
|
||||
## $ 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/)
|
||||
186
03_install/3.3_fedora.md
Normal file
186
03_install/3.3_fedora.md
Normal file
@@ -0,0 +1,186 @@
|
||||
## 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/)。
|
||||
217
03_install/3.4_centos.md
Normal file
217
03_install/3.4_centos.md
Normal file
@@ -0,0 +1,217 @@
|
||||
## 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
|
||||
203
03_install/3.5_raspberry-pi.md
Normal file
203
03_install/3.5_raspberry-pi.md
Normal file
@@ -0,0 +1,203 @@
|
||||
## 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=$ signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/raspbian \
|
||||
|
||||
## $ stable" | sudo tee /etc/apt/sources.list.d/docker.list > /dev/null
|
||||
|
||||
```
|
||||
|
||||
>以上命令会添加稳定版本的 Docker APT 源,如果需要测试版本的 Docker 请将 stable 改为 test。
|
||||
|
||||
#### 报错解决办法
|
||||
|
||||
在 `Raspberry Pi OS Bullseye/Bookworm` 中,如果你使用 `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=$ signed-by=/etc/apt/keyrings/docker.gpg] https://download.docker.com/linux/raspbian $ 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)。
|
||||
262
03_install/3.6_offline.md
Normal file
262
03_install/3.6_offline.md
Normal file
@@ -0,0 +1,262 @@
|
||||
## 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
|
||||
```
|
||||
76
03_install/3.7_mac.md
Normal file
76
03_install/3.7_mac.md
Normal file
@@ -0,0 +1,76 @@
|
||||
## 3.7 macOS
|
||||
|
||||
### 3.7.1 系统要求
|
||||
|
||||
[Docker Desktop for Mac](https://docs.docker.com/docker-for-mac/) 要求系统最低为 macOS Sonoma 14.0 或更高版本,建议升级到最新版本的 macOS。
|
||||
|
||||
### 3.7.2 安装
|
||||
|
||||
> [!WARNING]
|
||||
> **商业许可限制**:自 2021 年起,Docker Desktop 对微型企业(少于 250 名员工且年收入少于 1000 万美元)、个人使用、教育和非商业开源项目仍然免费。对于其他商业用途,需要付费订阅。企业用户请注意合规风险,或考虑使用开源替代方案。
|
||||
|
||||
Docker Desktop 为 Mac 用户提供了无缝的 Docker 体验。你可以选择使用 Homebrew 或手动下载安装包进行安装。
|
||||
|
||||
#### 使用 Homebrew 安装
|
||||
|
||||
[Homebrew](https://brew.sh/) 的 [Cask](https://github.com/Homebrew/homebrew-cask) 已经支持 Docker Desktop for Mac,因此可以很方便的使用 Homebrew Cask 来进行安装:
|
||||
|
||||
```bash
|
||||
$ brew install --cask docker
|
||||
```
|
||||
|
||||
#### 手动下载安装
|
||||
|
||||
如果需要手动下载,请点击以下[链接](https://desktop.docker.com/mac/main/amd64/Docker.dmg)下载 Docker Desktop for Mac。
|
||||
|
||||
> 如果你的电脑搭载的是 Apple Silicon 芯片 (`arm64` 架构),请点击以下[链接](https://desktop.docker.com/mac/main/arm64/Docker.dmg)下载 Docker Desktop for Mac。你可以在[官方文档](https://docs.docker.com/docker-for-mac/apple-silicon/)查阅已知的问题。
|
||||
|
||||
如同 macOS 其它软件一样,安装也非常简单,双击下载的 `.dmg` 文件,然后将那只叫 [Moby](https://www.docker.com/blog/call-me-moby-dock/) 的鲸鱼图标拖拽到 `Application` 文件夹即可 (其间需要输入用户密码)。
|
||||
|
||||

|
||||
|
||||
### 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/)
|
||||
51
03_install/3.8_windows.md
Normal file
51
03_install/3.8_windows.md
Normal file
@@ -0,0 +1,51 @@
|
||||
## 3.8 Windows 10/11
|
||||
|
||||
在 Windows 平台上,Docker Desktop 提供了完整的 Docker 开发环境。本节介绍在 Windows 10/11 上的安装和配置。
|
||||
|
||||
### 3.8.1 系统要求
|
||||
|
||||
[Docker Desktop for Windows](https://docs.docker.com/desktop/setup/install/windows-install/) 支持 64 位版本的 Windows 11 或 Windows 10 (需开启 Hyper-V),推荐使用 Windows 11。
|
||||
|
||||
### 3.8.2 安装
|
||||
|
||||
> [!WARNING]
|
||||
> **商业许可限制**:自 2021 年起,Docker Desktop 对微型企业(少于 250 名员工且年收入少于 1000 万美元)、个人使用、教育和非商业开源项目仍然免费。对于其他商业用途,需要付费订阅。企业用户请注意合规风险,或考虑使用开源替代方案。
|
||||
|
||||
**手动下载安装**
|
||||
|
||||
点击以下[链接](https://desktop.docker.com/win/main/amd64/Docker%20Desktop%20Installer.exe)下载 Docker Desktop for Windows。
|
||||
|
||||
下载好之后双击 `Docker Desktop Installer.exe` 开始安装。
|
||||
|
||||
**使用**[**winget**](https://docs.microsoft.com/zh-cn/windows/package-manager/)**安装**
|
||||
|
||||
```powershell
|
||||
$ winget install Docker.DockerDesktop
|
||||
```
|
||||
|
||||
### 3.8.3 在 WSL2 运行 Docker
|
||||
|
||||
若你的 Windows 版本为 Windows 10 专业版或家庭版 v1903 及以上版本可以使用 WSL2 运行 Docker,具体请查看 [Docker Desktop WSL 2 backend](https://docs.docker.com/docker-for-windows/wsl/)。
|
||||
|
||||
### 3.8.4 运行
|
||||
|
||||
在 Windows 搜索栏输入 **Docker** 点击 **Docker Desktop** 开始运行。
|
||||
|
||||

|
||||
|
||||
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/)
|
||||
BIN
03_install/3.9_mirror.md
Normal file
BIN
03_install/3.9_mirror.md
Normal file
Binary file not shown.
18
03_install/README.md
Normal file
18
03_install/README.md
Normal file
@@ -0,0 +1,18 @@
|
||||
# 第三章 安装 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)
|
||||
30
03_install/summary.md
Normal file
30
03_install/summary.md
Normal file
@@ -0,0 +1,30 @@
|
||||
## 本章小结
|
||||
|
||||
Docker 支持在多种平台上安装和使用,选择合适的安装方式是顺利使用 Docker 的第一步。
|
||||
|
||||
| 平台 | 推荐方式 | 说明 |
|
||||
|------|---------|------|
|
||||
| **Ubuntu/Debian** | 官方 APT 仓库 | 最完善的支持,推荐首选 |
|
||||
| **CentOS/Fedora** | 官方 YUM/DNF 仓库 | 注意关闭 SELinux 或配置策略 |
|
||||
| **macOS** | Docker Desktop | 图形化安装,包含 Compose 和 Kubernetes |
|
||||
| **Windows 10/11** | Docker Desktop (WSL 2) | 需启用 WSL 2 后端 |
|
||||
| **Raspberry Pi** | 官方安装脚本 | 支持 ARM 架构 |
|
||||
| **离线环境** | 二进制包安装 | 适用于无法联网的服务器 |
|
||||
|
||||
### 安装后验证
|
||||
|
||||
安装完成后,运行以下命令验证 Docker 是否正常工作:
|
||||
|
||||
```bash
|
||||
$ docker version
|
||||
$ docker run --rm hello-world
|
||||
```
|
||||
|
||||
### 延伸阅读
|
||||
|
||||
- [镜像加速器](3.9_mirror.md):解决国内拉取镜像慢的问题
|
||||
- [开启实验特性](3.10_experimental.md):使用最新功能
|
||||
- [Docker Hub](../06_repository/6.1_dockerhub.md):官方镜像仓库
|
||||
---
|
||||
|
||||
> 📝 **发现错误或有改进建议?** 欢迎提交 [Issue](https://github.com/yeasy/docker_practice/issues) 或 [PR](https://github.com/yeasy/docker_practice/pulls)。
|
||||
238
04_image/4.1_pull.md
Normal file
238
04_image/4.1_pull.md
Normal file
@@ -0,0 +1,238 @@
|
||||
## 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
|
||||
```
|
||||
|
||||
---
|
||||
267
04_image/4.2_list.md
Normal file
267
04_image/4.2_list.md
Normal file
@@ -0,0 +1,267 @@
|
||||
## 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
|
||||
```
|
||||
|
||||
---
|
||||
269
04_image/4.3_rm.md
Normal file
269
04_image/4.3_rm.md
Normal file
@@ -0,0 +1,269 @@
|
||||
## 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
|
||||
```
|
||||
|
||||
---
|
||||
131
04_image/4.4_commit.md
Normal file
131
04_image/4.4_commit.md
Normal file
@@ -0,0 +1,131 @@
|
||||
## 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` 制作镜像,以及后期修改的话,每一次修改都会让镜像更加臃肿一次,所删除的上一层的东西并不会丢失,会一直如影随形的跟着这个镜像,即使根本无法访问到。这会让镜像更加臃肿。
|
||||
201
04_image/4.5_build.md
Normal file
201
04_image/4.5_build.md
Normal file
@@ -0,0 +1,201 @@
|
||||
## 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` 的话,将会使其为上下文压缩包,直接将其展开,将里面视为上下文,并开始构建。
|
||||
85
04_image/4.6_other.md
Normal file
85
04_image/4.6_other.md
Normal file
@@ -0,0 +1,85 @@
|
||||
## 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'
|
||||
```
|
||||
65
04_image/4.7_internal.md
Normal file
65
04_image/4.7_internal.md
Normal file
@@ -0,0 +1,65 @@
|
||||
## 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)** 章节。
|
||||
22
04_image/README.md
Normal file
22
04_image/README.md
Normal file
@@ -0,0 +1,22 @@
|
||||
# 第四章 使用镜像
|
||||
|
||||
在之前的介绍中,我们知道镜像是 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`)时,其元数据能够与镜像数据一并完好地管理于本地存储系统中,为供应链安全验证补齐了最后一块拼图。
|
||||
16
04_image/demo/buildkit/Dockerfile
Normal file
16
04_image/demo/buildkit/Dockerfile
Normal file
@@ -0,0 +1,16 @@
|
||||
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
|
||||
37
04_image/demo/buildkit/Dockerfile.buildkit
Normal file
37
04_image/demo/buildkit/Dockerfile.buildkit
Normal file
@@ -0,0 +1,37 @@
|
||||
# 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 .
|
||||
1
04_image/demo/buildkit/aws.txt
Normal file
1
04_image/demo/buildkit/aws.txt
Normal file
@@ -0,0 +1 @@
|
||||
awskey
|
||||
11
04_image/demo/buildkit/package.json
Normal file
11
04_image/demo/buildkit/package.json
Normal file
@@ -0,0 +1,11 @@
|
||||
{
|
||||
"name": "my_app",
|
||||
"version": "19.6.0",
|
||||
"devDependencies": {
|
||||
"webpack": "*",
|
||||
"webpack-cli": "*"
|
||||
},
|
||||
"scripts": {
|
||||
"build": "mkdir -p $PWD/dist && cp -r src/* dist/"
|
||||
}
|
||||
}
|
||||
1
04_image/demo/buildkit/src/index.js
Normal file
1
04_image/demo/buildkit/src/index.js
Normal file
@@ -0,0 +1 @@
|
||||
console.log(1);
|
||||
5
04_image/demo/multi-arch/Dockerfile
Normal file
5
04_image/demo/multi-arch/Dockerfile
Normal file
@@ -0,0 +1,5 @@
|
||||
FROM --platform=$TARGETPLATFORM alpine
|
||||
|
||||
RUN uname -a > /os.txt
|
||||
|
||||
CMD cat /os.txt
|
||||
1
04_image/demo/multistage-builds/.gitignore
vendored
Normal file
1
04_image/demo/multistage-builds/.gitignore
vendored
Normal file
@@ -0,0 +1 @@
|
||||
app
|
||||
21
04_image/demo/multistage-builds/Dockerfile
Normal file
21
04_image/demo/multistage-builds/Dockerfile
Normal file
@@ -0,0 +1,21 @@
|
||||
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"]
|
||||
10
04_image/demo/multistage-builds/Dockerfile.build
Normal file
10
04_image/demo/multistage-builds/Dockerfile.build
Normal file
@@ -0,0 +1,10 @@
|
||||
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 .
|
||||
9
04_image/demo/multistage-builds/Dockerfile.copy
Normal file
9
04_image/demo/multistage-builds/Dockerfile.copy
Normal file
@@ -0,0 +1,9 @@
|
||||
FROM alpine:latest
|
||||
|
||||
RUN apk --no-cache add ca-certificates
|
||||
|
||||
WORKDIR /root/
|
||||
|
||||
COPY app .
|
||||
|
||||
CMD ["./app"]
|
||||
15
04_image/demo/multistage-builds/Dockerfile.one
Normal file
15
04_image/demo/multistage-builds/Dockerfile.one
Normal file
@@ -0,0 +1,15 @@
|
||||
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"]
|
||||
7
04_image/demo/multistage-builds/app.go
Normal file
7
04_image/demo/multistage-builds/app.go
Normal file
@@ -0,0 +1,7 @@
|
||||
package main
|
||||
|
||||
import "fmt"
|
||||
|
||||
func main(){
|
||||
fmt.Printf("Hello World!");
|
||||
}
|
||||
14
04_image/demo/multistage-builds/build.sh
Normal file
14
04_image/demo/multistage-builds/build.sh
Normal file
@@ -0,0 +1,14 @@
|
||||
#!/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
|
||||
36
04_image/summary.md
Normal file
36
04_image/summary.md
Normal file
@@ -0,0 +1,36 @@
|
||||
## 本章小结
|
||||
|
||||
本章介绍了 Docker 镜像的获取、列出、删除以及构建方式。
|
||||
|
||||
| 操作 | 命令 |
|
||||
|------|------|
|
||||
| 拉取镜像 | `docker pull 镜像名:标签` |
|
||||
| 拉取所有标签 | `docker pull -a 镜像名` |
|
||||
| 指定平台 | `docker pull --platform linux/amd64 镜像名` |
|
||||
| 用摘要拉取 | `docker pull 镜像名@sha256:...` |
|
||||
| 列出所有镜像 | `docker images` |
|
||||
| 按仓库名过滤 | `docker images nginx` |
|
||||
| 列出虚悬镜像 | `docker images -f dangling=true` |
|
||||
| 只输出 ID | `docker images -q` |
|
||||
| 显示摘要 | `docker images --digests` |
|
||||
| 自定义格式 | `docker images --format "..."` |
|
||||
| 查看空间占用 | `docker system df` |
|
||||
| 删除指定镜像 | `docker rmi 镜像名:标签` |
|
||||
| 强制删除 | `docker rmi -f 镜像名` |
|
||||
| 删除虚悬镜像 | `docker image prune` |
|
||||
| 删除未使用镜像 | `docker image prune -a` |
|
||||
| 批量删除 | `docker rmi $(docker images -q -f ...)` |
|
||||
|
||||
### 延伸阅读
|
||||
|
||||
- [获取镜像](4.1_pull.md):从 Registry 拉取镜像
|
||||
- [列出镜像](4.2_list.md):查看和过滤镜像
|
||||
- [删除镜像](4.3_rm.md):清理本地镜像
|
||||
- [镜像加速器](../03_install/3.9_mirror.md):加速镜像下载
|
||||
- [Docker Hub](../06_repository/6.1_dockerhub.md):官方镜像仓库
|
||||
- [镜像](../02_basic_concept/2.1_image.md):理解镜像概念
|
||||
- [删除容器](../05_container/5.6_rm.md):清理容器
|
||||
- [数据卷](../08_data/8.1_volume.md):清理数据卷
|
||||
---
|
||||
|
||||
> 📝 **发现错误或有改进建议?** 欢迎提交 [Issue](https://github.com/yeasy/docker_practice/issues) 或 [PR](https://github.com/yeasy/docker_practice/pulls)。
|
||||
225
05_container/5.1_run.md
Normal file
225
05_container/5.1_run.md
Normal file
@@ -0,0 +1,225 @@
|
||||
## 5.1 启动
|
||||
|
||||
本节将详细介绍 Docker 容器的启动方式,包括新建启动和重新启动已停止的容器。
|
||||
|
||||
### 5.1.1 启动方式概述
|
||||
|
||||
启动容器有两种方式:
|
||||
|
||||
- **新建并启动**:基于镜像创建新容器
|
||||
- **重新启动**:将已终止的容器重新运行
|
||||
|
||||
由于 Docker 容器非常轻量,实际使用中常常是随时删除和新建容器,而不是反复重启同一个容器。
|
||||
|
||||
### 5.1.2 新建并启动
|
||||
|
||||
#### 基本语法
|
||||
|
||||
```bash
|
||||
docker run [选项] 镜像 [命令] [参数...]
|
||||
```
|
||||
|
||||
#### 最简单的例子
|
||||
|
||||
输出 “Hello World” 后容器自动终止:
|
||||
|
||||
```bash
|
||||
$ docker run ubuntu:24.04 /bin/echo 'Hello world'
|
||||
Hello world
|
||||
```
|
||||
|
||||
这与直接执行 `/bin/echo 'Hello world'` 几乎没有区别,但实际上已经启动了一个完整的 Ubuntu 容器来执行这条命令。
|
||||
|
||||
#### 交互式容器
|
||||
|
||||
启动一个可以交互的 bash 终端:
|
||||
|
||||
```bash
|
||||
$ docker run -it ubuntu:24.04 /bin/bash
|
||||
root@af8bae53bdd3:/#
|
||||
```
|
||||
|
||||
**参数说明**:
|
||||
|
||||
| 参数 | 作用 |
|
||||
|------|------|
|
||||
| `-i` | 保持标准输入 (stdin) 打开,允许输入 |
|
||||
| `-t` | 分配伪终端 (pseudo-TTY),提供终端界面 |
|
||||
| `-it` | 两者组合使用,获得交互式终端 |
|
||||
|
||||
在交互模式下可以执行命令:
|
||||
|
||||
```bash
|
||||
root@af8bae53bdd3:/# pwd
|
||||
/
|
||||
root@af8bae53bdd3:/# ls
|
||||
bin boot dev etc home lib lib64 media mnt opt proc root run sbin srv sys tmp usr var
|
||||
root@af8bae53bdd3:/# exit # 退出容器
|
||||
```
|
||||
|
||||
### 5.1.3 docker run 的完整流程
|
||||
|
||||
执行 `docker run` 时,Docker 在后台完成以下操作:
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
Cmd["docker run ubuntu:24.04 /bin/echo 'Hello'"] --> Step1
|
||||
|
||||
Step1{"1. 检查本地是否有 ubuntu:24.04 镜像"}
|
||||
Step1 -- 有 --> Step1_Yes["使用本地镜像"]
|
||||
Step1 -- 无 --> Step1_No["从 Registry 下载"]
|
||||
|
||||
Step1_Yes --> Step2
|
||||
Step1_No --> Step2
|
||||
|
||||
Step2["2. 创建容器<br/>• 基于镜像的只读层<br/>• 添加一层可读写层(容器存储层)"] --> Step3
|
||||
Step3["3. 配置网络<br/>• 创建虚拟网卡<br/>• 分配 IP 地址<br/>• 连接到 Docker 网桥"] --> Step4
|
||||
Step4["4. 启动容器,执行指定命令"] --> Step5
|
||||
Step5["5. 命令执行完毕,容器停止"]
|
||||
```
|
||||
|
||||
### 5.1.4 常用启动选项
|
||||
|
||||
#### 基础选项
|
||||
|
||||
| 选项 | 说明 | 示例 |
|
||||
|------|------|------|
|
||||
| `-d` | 后台运行 (detach)| `docker run -d nginx` |
|
||||
| `-it` | 交互式终端 | `docker run -it ubuntu bash` |
|
||||
| `--name` | 指定容器名称 | `docker run --name myapp nginx` |
|
||||
| `--rm` | 退出后自动删除容器 | `docker run --rm ubuntu echo hi` |
|
||||
|
||||
#### 端口映射
|
||||
|
||||
```bash
|
||||
## 将容器的 80 端口映射到宿主机的 8080 端口
|
||||
|
||||
$ docker run -d -p 8080:80 nginx
|
||||
|
||||
## 随机映射端口
|
||||
|
||||
$ docker run -d -P nginx
|
||||
|
||||
## 只绑定到 localhost
|
||||
|
||||
$ docker run -d -p 127.0.0.1:8080:80 nginx
|
||||
```
|
||||
|
||||
#### 数据卷挂载
|
||||
|
||||
```bash
|
||||
## 挂载命名卷
|
||||
|
||||
$ docker run -v mydata:/data nginx
|
||||
|
||||
## 挂载宿主机目录
|
||||
|
||||
$ docker run -v /host/path:/container/path nginx
|
||||
|
||||
## 只读挂载
|
||||
|
||||
$ docker run -v /host/path:/container/path:ro nginx
|
||||
```
|
||||
|
||||
#### 环境变量
|
||||
|
||||
```bash
|
||||
## 设置单个环境变量
|
||||
|
||||
$ docker run -e MYSQL_ROOT_PASSWORD=secret mysql
|
||||
|
||||
## 从文件加载环境变量
|
||||
|
||||
$ docker run --env-file .env myapp
|
||||
```
|
||||
|
||||
#### 资源限制
|
||||
|
||||
```bash
|
||||
## 限制内存
|
||||
|
||||
$ docker run -m 512m nginx
|
||||
|
||||
## 限制 CPU
|
||||
|
||||
$ docker run --cpus=1.5 nginx
|
||||
```
|
||||
|
||||
### 5.1.5 启动已终止容器
|
||||
|
||||
使用 `docker start` 重新启动已停止的容器:
|
||||
|
||||
```bash
|
||||
## 查看所有容器(包括已停止的)
|
||||
|
||||
$ docker ps -a
|
||||
CONTAINER ID IMAGE STATUS NAMES
|
||||
af8bae53bdd3 ubuntu Exited (0) 2 minutes ago myubuntu
|
||||
|
||||
## 重新启动
|
||||
|
||||
$ docker start myubuntu
|
||||
|
||||
## 启动并附加终端
|
||||
|
||||
$ docker start -ai myubuntu
|
||||
```
|
||||
|
||||
### 5.1.6 容器内进程的特点
|
||||
|
||||
容器内只运行指定的应用程序及其必需资源:
|
||||
|
||||
```bash
|
||||
root@ba267838cc1b:/# ps
|
||||
PID TTY TIME CMD
|
||||
1 ? 00:00:00 bash
|
||||
11 ? 00:00:00 ps
|
||||
```
|
||||
|
||||
可见容器中仅运行了 `bash` 进程。这种特点使得 Docker 对资源的利用率极高。
|
||||
|
||||
> 💡 笔者提示:容器内的 PID 1 进程很重要——它是容器的主进程,该进程退出则容器停止。详见[后台运行](5.2_daemon.md)章节。
|
||||
|
||||
### 5.1.7 常见问题
|
||||
|
||||
#### Q:容器启动后立即退出
|
||||
|
||||
**原因**:主进程执行完毕或无法保持运行
|
||||
|
||||
```bash
|
||||
## 这个容器会立即退出(echo 执行完就结束了)
|
||||
|
||||
$ docker run ubuntu echo "hello"
|
||||
|
||||
## 解决:使用能持续运行的命令
|
||||
|
||||
$ docker run -d nginx # nginx 是持续运行的服务
|
||||
```
|
||||
|
||||
详细解释见[后台运行](5.2_daemon.md)。
|
||||
|
||||
#### Q:无法连接容器内的服务
|
||||
|
||||
**原因**:未正确映射端口
|
||||
|
||||
```bash
|
||||
## 错误:没有 -p 参数,外部无法访问
|
||||
|
||||
$ docker run -d nginx
|
||||
|
||||
## 正确:映射端口
|
||||
|
||||
$ docker run -d -p 80:80 nginx
|
||||
```
|
||||
|
||||
#### Q:容器内修改的文件丢失
|
||||
|
||||
**原因**:未使用数据卷,数据保存在容器存储层
|
||||
|
||||
```bash
|
||||
## 使用数据卷持久化
|
||||
|
||||
$ docker run -v mydata:/app/data myapp
|
||||
```
|
||||
|
||||
详见[数据管理](../08_data/README.md)。
|
||||
234
05_container/5.2_daemon.md
Normal file
234
05_container/5.2_daemon.md
Normal file
@@ -0,0 +1,234 @@
|
||||
## 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):管理多个后台容器的更好方式
|
||||
233
05_container/5.3_stop.md
Normal file
233
05_container/5.3_stop.md
Normal file
@@ -0,0 +1,233 @@
|
||||
## 5.3 终止
|
||||
|
||||
本节将介绍如何终止一个运行中的容器,以及几种不同的终止方式及其区别。
|
||||
|
||||
### 5.3.1 终止方式概述
|
||||
|
||||
终止容器有三种方式:
|
||||
|
||||
| 方式 | 命令 | 说明 |
|
||||
|------|------|------|
|
||||
| **优雅停止** | `docker stop` | 先发 SIGTERM,超时后发 SIGKILL |
|
||||
| **强制停止** | `docker kill` | 直接发 SIGKILL |
|
||||
| **自动终止** | - | 容器主进程退出时自动停止 |
|
||||
|
||||
---
|
||||
|
||||
### 5.3.2 docker stop (推荐)
|
||||
|
||||
#### docker stop 基本用法
|
||||
|
||||
```bash
|
||||
$ docker stop 容器名或ID
|
||||
```
|
||||
|
||||
#### 工作原理
|
||||
|
||||
```mermaid
|
||||
flowchart TD
|
||||
cmd["docker stop mycontainer"] --> A["1. 发送 SIGTERM 信号给容器主进程 (PID 1)"]
|
||||
A --> B["2. 等待容器优雅退出 (默认 10 秒)"]
|
||||
B --> C["3. 如果超时仍未退出,发送 SIGKILL 强制终止"]
|
||||
```
|
||||
|
||||
#### 自定义超时时间
|
||||
|
||||
```bash
|
||||
## 等待 30 秒后强制终止
|
||||
|
||||
$ docker stop -t 30 mycontainer
|
||||
|
||||
## 立即发送 SIGKILL(相当于 docker kill)
|
||||
|
||||
$ docker stop -t 0 mycontainer
|
||||
```
|
||||
|
||||
#### 停止多个容器
|
||||
|
||||
```bash
|
||||
## 停止多个指定容器
|
||||
|
||||
$ docker stop container1 container2 container3
|
||||
|
||||
## 停止所有运行中的容器
|
||||
|
||||
$ docker stop $(docker ps -q)
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.3.3 docker kill
|
||||
|
||||
#### 基本用法
|
||||
|
||||
```bash
|
||||
$ docker kill 容器名或ID
|
||||
```
|
||||
|
||||
#### 与 stop 的区别
|
||||
|
||||
| 命令 | 信号 | 使用场景 |
|
||||
|------|------|---------|
|
||||
| `docker stop` | SIGTERM → SIGKILL | 正常停止,让应用优雅退出 |
|
||||
| `docker kill` | SIGKILL | 应用无响应,强制终止 |
|
||||
|
||||
#### 发送自定义信号
|
||||
|
||||
```bash
|
||||
## 发送 SIGHUP(让进程重新加载配置)
|
||||
|
||||
$ docker kill -s HUP mycontainer
|
||||
|
||||
## 发送 SIGTERM
|
||||
|
||||
$ docker kill -s TERM mycontainer
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.3.4 容器自动终止
|
||||
|
||||
容器的生命周期与主进程绑定。主进程退出时,容器自动停止:
|
||||
|
||||
```bash
|
||||
## 主进程是交互式 bash
|
||||
|
||||
$ docker run -it ubuntu bash
|
||||
root@abc123:/# exit # 退出 bash → 容器停止
|
||||
|
||||
## 主进程执行完毕
|
||||
|
||||
$ docker run ubuntu echo "Hello" # echo 执行完 → 容器停止
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.3.5 查看已停止的容器
|
||||
|
||||
```bash
|
||||
$ docker ps -a
|
||||
CONTAINER ID IMAGE COMMAND STATUS NAMES
|
||||
ba267838cc1b ubuntu "/bin/bash" Exited (0) 2 minutes ago myubuntu
|
||||
c5d3a5e8f7b2 nginx "nginx" Up 5 minutes mynginx
|
||||
```
|
||||
|
||||
**STATUS 字段说明**:
|
||||
|
||||
| 状态 | 说明 |
|
||||
|------|------|
|
||||
| `Up X minutes` | 运行中 |
|
||||
| `Exited (0)` | 正常退出 (退出码 0)|
|
||||
| `Exited (1)` | 异常退出 (非零退出码)|
|
||||
| `Exited (137)` | 被 SIGKILL 终止 (128 + 9)|
|
||||
| `Exited (143)` | 被 SIGTERM 终止 (128 + 15)|
|
||||
|
||||
---
|
||||
|
||||
### 5.3.6 重新启动容器
|
||||
|
||||
#### 启动已停止的容器
|
||||
|
||||
```bash
|
||||
$ docker start 容器名或ID
|
||||
|
||||
## 启动并附加终端
|
||||
|
||||
$ docker start -ai 容器名
|
||||
```
|
||||
|
||||
#### 重启运行中的容器
|
||||
|
||||
```bash
|
||||
## 先停止再启动
|
||||
|
||||
$ docker restart 容器名
|
||||
|
||||
## 自定义停止超时
|
||||
|
||||
$ docker restart -t 30 容器名
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.3.7 生命周期状态图
|
||||
|
||||
```mermaid
|
||||
stateDiagram-v2
|
||||
direction TB
|
||||
[*] --> Created : docker create
|
||||
Created --> Running : docker start
|
||||
Running --> Stopped : docker stop
|
||||
Running --> Paused : docker pause
|
||||
Paused --> Running : docker unpause
|
||||
|
||||
Created --> Deleted : docker rm
|
||||
Stopped --> Deleted : docker rm
|
||||
Paused --> Deleted : docker rm
|
||||
|
||||
Deleted --> [*]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.3.8 批量操作
|
||||
|
||||
#### 停止所有容器
|
||||
|
||||
```bash
|
||||
$ docker stop $(docker ps -q)
|
||||
```
|
||||
|
||||
#### 删除所有已停止的容器
|
||||
|
||||
```bash
|
||||
$ docker container prune
|
||||
```
|
||||
|
||||
#### 停止并删除所有容器
|
||||
|
||||
```bash
|
||||
$ docker stop $(docker ps -q) && docker container prune -f
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.3.9 常见问题
|
||||
|
||||
#### Q:容器停止很慢
|
||||
|
||||
原因:应用没有正确处理 SIGTERM 信号,需要等待超时后强制终止。
|
||||
|
||||
解决方案:
|
||||
|
||||
1. 在应用中正确处理 SIGTERM
|
||||
2. 使用 `docker stop -t 0` 立即终止
|
||||
3. 检查 Dockerfile 中的 `STOPSIGNAL` 配置
|
||||
|
||||
#### Q:如何让容器优雅退出
|
||||
|
||||
确保容器主进程正确处理信号:
|
||||
|
||||
```dockerfile
|
||||
## Dockerfile 示例
|
||||
|
||||
FROM node:22
|
||||
...
|
||||
## 使用 exec 形式确保信号能传递给 node 进程
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
```
|
||||
|
||||
#### Q:容器无法停止
|
||||
|
||||
```bash
|
||||
## 强制终止
|
||||
|
||||
$ docker kill 容器名
|
||||
|
||||
## 如果仍无法停止,检查系统资源
|
||||
|
||||
$ docker inspect 容器名
|
||||
```
|
||||
|
||||
---
|
||||
282
05_container/5.4_attach_exec.md
Normal file
282
05_container/5.4_attach_exec.md
Normal file
@@ -0,0 +1,282 @@
|
||||
## 5.4 进入容器
|
||||
|
||||
### 5.4.1 为什么需要进入容器
|
||||
|
||||
使用 `-d` 参数启动容器后,容器在后台运行。以下场景需要进入容器内部操作:
|
||||
|
||||
| 场景 | 示例 |
|
||||
|------|------|
|
||||
| **调试问题** | 查看日志、检查配置、排查错误 |
|
||||
| **临时操作** | 执行数据库迁移、清理缓存 |
|
||||
| **检查状态** | 查看进程、网络连接、文件系统 |
|
||||
| **开发测试** | 交互式测试命令、验证环境 |
|
||||
|
||||
### 5.4.2 两种进入方式
|
||||
|
||||
Docker 提供两种进入容器的命令:
|
||||
|
||||
| 命令 | 推荐程度 | 特点 |
|
||||
|------|---------|------|
|
||||
| `docker exec` | ✅ **推荐** | 启动新进程,退出不影响容器 |
|
||||
| `docker attach` | ⚠️ 谨慎使用 | 附加到主进程,退出可能停止容器 |
|
||||
|
||||
---
|
||||
|
||||
### 5.4.3 docker exec (推荐)
|
||||
|
||||
#### docker exec 基本用法
|
||||
|
||||
```bash
|
||||
## 进入容器并启动交互式 shell
|
||||
|
||||
$ docker exec -it 容器名 /bin/bash
|
||||
|
||||
## 或使用 sh(适用于 Alpine 等精简镜像)
|
||||
|
||||
$ docker exec -it 容器名 /bin/sh
|
||||
```
|
||||
|
||||
#### 参数说明
|
||||
|
||||
| 参数 | 作用 |
|
||||
|------|------|
|
||||
| `-i` | 保持标准输入打开 (interactive)|
|
||||
| `-t` | 分配伪终端 (TTY)|
|
||||
| `-it` | 两者组合,获得完整交互体验 |
|
||||
| `-u` | 指定用户 (如 `-u root`)|
|
||||
| `-w` | 指定工作目录 |
|
||||
| `-e` | 设置环境变量 |
|
||||
|
||||
#### docker exec 示例
|
||||
|
||||
```bash
|
||||
## 启动一个后台容器
|
||||
|
||||
$ docker run -dit --name myubuntu ubuntu
|
||||
69d137adef7a...
|
||||
|
||||
## 进入容器(交互式 shell)
|
||||
|
||||
$ docker exec -it myubuntu bash
|
||||
root@69d137adef7a:/# ls
|
||||
bin boot dev etc home lib ...
|
||||
root@69d137adef7a:/# exit
|
||||
|
||||
## 容器仍在运行!
|
||||
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE STATUS NAMES
|
||||
69d137adef7a ubuntu Up 2 minutes myubuntu
|
||||
```
|
||||
|
||||
#### 执行单条命令
|
||||
|
||||
不进入交互模式,直接执行命令:
|
||||
|
||||
```bash
|
||||
## 查看容器内进程
|
||||
|
||||
$ docker exec myubuntu ps aux
|
||||
|
||||
## 查看配置文件
|
||||
|
||||
$ docker exec myubuntu cat /etc/nginx/nginx.conf
|
||||
|
||||
## 以 root 用户执行
|
||||
|
||||
$ docker exec -u root myubuntu apt update
|
||||
```
|
||||
|
||||
#### 只用 -i 不用 -t 的区别
|
||||
|
||||
```bash
|
||||
## 只用 -i:可以执行命令,但没有提示符
|
||||
|
||||
$ docker exec -i myubuntu bash
|
||||
ls # 输入命令
|
||||
bin # 输出结果
|
||||
boot
|
||||
dev
|
||||
...
|
||||
|
||||
## 用 -it:有完整的终端体验
|
||||
|
||||
$ docker exec -it myubuntu bash
|
||||
root@69d137adef7a:/# # 有提示符
|
||||
```
|
||||
|
||||
> 💡 通常使用 `-it` 组合。只有在脚本中需要通过管道传入命令时才只用 `-i`。
|
||||
|
||||
---
|
||||
|
||||
### 5.4.4 docker attach (谨慎使用)
|
||||
|
||||
#### docker attach 基本用法
|
||||
|
||||
```bash
|
||||
$ docker attach 容器名
|
||||
```
|
||||
|
||||
#### 工作原理
|
||||
|
||||
`attach` 会附加到容器的 **主进程** (PID 1) 的标准输入输出:
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Container ["容器"]
|
||||
direction TB
|
||||
subgraph Process ["主进程"]
|
||||
P1["PID 1: /bin/bash<br>(你的输入直接发送到主进程)"]
|
||||
end
|
||||
end
|
||||
Attach["docker attach"] -->|"附加到这里"| P1
|
||||
```
|
||||
|
||||
#### docker attach 示例
|
||||
|
||||
```bash
|
||||
## 启动容器
|
||||
|
||||
$ docker run -dit --name myubuntu ubuntu
|
||||
243c32535da7...
|
||||
|
||||
## 附加到容器
|
||||
|
||||
$ docker attach myubuntu
|
||||
root@243c32535da7:/#
|
||||
```
|
||||
|
||||
#### ⚠️ 重要警告
|
||||
|
||||
**从 attach 会话中输入 `exit` 或按 `Ctrl+D` 会导致容器停止!**
|
||||
|
||||
```bash
|
||||
$ docker attach myubuntu
|
||||
root@243c32535da7:/# exit # 这会停止容器!
|
||||
|
||||
$ docker ps
|
||||
CONTAINER ID IMAGE STATUS NAMES
|
||||
243c32535da7 ubuntu Exited (0) 2 seconds ago myubuntu
|
||||
```
|
||||
|
||||
**原因**:attach 附加到主进程,退出主进程就等于退出容器。
|
||||
|
||||
#### 安全退出 attach
|
||||
|
||||
使用 `Ctrl+P` 然后 `Ctrl+Q` 可以从 attach 会话中 **分离**,而不停止容器:
|
||||
|
||||
```bash
|
||||
$ docker attach myubuntu
|
||||
root@243c32535da7:/#
|
||||
## 按 Ctrl+P 然后 Ctrl+Q
|
||||
|
||||
read escape sequence
|
||||
|
||||
$ docker ps # 容器仍在运行
|
||||
CONTAINER ID IMAGE STATUS NAMES
|
||||
243c32535da7 ubuntu Up 5 minutes myubuntu
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.4.5 exec vs attach 对比
|
||||
|
||||
| 特性 | docker exec | docker attach |
|
||||
|------|-------------|---------------|
|
||||
| **工作方式** | 在容器内启动新进程 | 附加到主进程 |
|
||||
| **退出影响** | 不影响容器 | 可能停止容器 |
|
||||
| **多终端** | 可以开多个 | 共享同一个会话 |
|
||||
| **适用场景** | 调试、临时操作 | 查看主进程输出 |
|
||||
| **推荐程度** | ✅ 推荐 | ⚠️ 特殊场景使用 |
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph Exec ["docker exec"]
|
||||
direction TB
|
||||
subgraph Container1 ["容器"]
|
||||
E_PID1["PID 1: nginx"]
|
||||
E_PID50["PID 50: bash"]
|
||||
end
|
||||
NewProc["新进程"] -- 附加到 --> E_PID50
|
||||
end
|
||||
|
||||
subgraph Attach ["docker attach"]
|
||||
direction TB
|
||||
subgraph Container2 ["容器"]
|
||||
A_PID1["PID 1: bash"]
|
||||
end
|
||||
MainProc["附加到主进程"] --> A_PID1
|
||||
end
|
||||
|
||||
note1["退出 bash 不影响 nginx"]
|
||||
note2["退出 bash 容器停止"]
|
||||
Container1 -.-> note1
|
||||
Container2 -.-> note2
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.4.6 最佳实践
|
||||
|
||||
#### 1. 首选 docker exec
|
||||
|
||||
```bash
|
||||
## 进入容器调试
|
||||
|
||||
$ docker exec -it myapp bash
|
||||
|
||||
## 查看日志
|
||||
|
||||
$ docker exec myapp tail -f /var/log/app.log
|
||||
|
||||
## 执行数据库迁移
|
||||
|
||||
$ docker exec myapp python manage.py migrate
|
||||
```
|
||||
|
||||
#### 2. 生产环境避免进入容器
|
||||
|
||||
笔者建议:生产环境应尽量避免进入容器直接操作,而是通过:
|
||||
|
||||
- 日志系统查看日志 (如 `docker logs` 或集中式日志)
|
||||
- 监控系统查看状态
|
||||
- 重新部署而非手动修改
|
||||
|
||||
#### 3. 无 shell 镜像的处理
|
||||
|
||||
某些精简镜像 (如基于 `scratch` 或 `distroless`) 没有 shell:
|
||||
|
||||
```bash
|
||||
## 这会失败
|
||||
|
||||
$ docker exec -it myapp bash
|
||||
OCI runtime exec failed: exec failed: unable to start container process: exec: "bash": executable file not found
|
||||
|
||||
## 解决方案:使用调试容器(Docker Desktop 或 Kubernetes debug)
|
||||
|
||||
$ docker debug myapp
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 5.4.7 常见问题
|
||||
|
||||
#### Q:exec 进入后看不到其他终端的操作
|
||||
|
||||
这是正常的。exec 启动的是独立进程,多个 exec 会话互不影响。
|
||||
|
||||
#### Q:容器没有 bash
|
||||
|
||||
尝试使用 sh:
|
||||
|
||||
```bash
|
||||
$ docker exec -it myapp /bin/sh
|
||||
```
|
||||
|
||||
#### Q:需要 root 权限
|
||||
|
||||
```bash
|
||||
$ docker exec -u root -it myapp bash
|
||||
```
|
||||
|
||||
---
|
||||
@@ -1,28 +1,34 @@
|
||||
##导出和导入容器
|
||||
## 5.5 导出和导入
|
||||
|
||||
当我们需要迁移容器或者备份容器时,可以使用 Docker 的导入和导出功能。本节将介绍这两个命令的使用方法。
|
||||
|
||||
### 5.5.1 导出容器
|
||||
|
||||
###导出容器
|
||||
如果要导出本地某个容器,可以使用 `docker export` 命令。
|
||||
```
|
||||
$ sudo docker ps -a
|
||||
```bash
|
||||
$ docker container ls -a
|
||||
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
|
||||
7691a814370e ubuntu:14.04 "/bin/bash" 36 hours ago Exited (0) 21 hours ago test
|
||||
$ sudo docker export 7691a814370e > ubuntu.tar
|
||||
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` 从容器快照文件中再导入为镜像,例如
|
||||
```
|
||||
$ cat ubuntu.tar | sudo docker import - test/ubuntu:v1.0
|
||||
$ sudo docker images
|
||||
|
||||
```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 或者某个目录来导入,例如
|
||||
```
|
||||
$sudo docker import http://example.com/exampleimage.tgz example/imagerepo
|
||||
|
||||
```bash
|
||||
$ docker import http://example.com/exampleimage.tgz example/imagerepo
|
||||
```
|
||||
|
||||
*注:用户既可以使用 `docker load` 来导入镜像存储文件到本地镜像库,也可以使用 `docker import` 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息(即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。
|
||||
|
||||
|
||||
*注:用户既可以使用 `docker load` 来导入镜像存储文件到本地镜像库,也可以使用 `docker import` 来导入一个容器快照到本地镜像库。这两者的区别在于容器快照文件将丢弃所有的历史记录和元数据信息 (即仅保存容器当时的快照状态),而镜像存储文件将保存完整记录,体积也要大。此外,从容器快照文件导入时可以重新指定标签等元数据信息。*
|
||||
246
05_container/5.6_rm.md
Normal file
246
05_container/5.6_rm.md
Normal file
@@ -0,0 +1,246 @@
|
||||
## 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
|
||||
```
|
||||
|
||||
---
|
||||
14
05_container/README.md
Normal file
14
05_container/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# 第五章 操作容器
|
||||
|
||||
容器是 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)
|
||||
33
05_container/summary.md
Normal file
33
05_container/summary.md
Normal file
@@ -0,0 +1,33 @@
|
||||
## 本章小结
|
||||
|
||||
本章介绍了 Docker 容器的启动、停止、进入和删除等核心操作。
|
||||
|
||||
| 操作 | 命令 | 说明 |
|
||||
|------|------|------|
|
||||
| 新建并运行 | `docker run` | 最常用的启动方式 |
|
||||
| 交互式启动 | `docker run -it` | 用于调试或临时操作 |
|
||||
| 后台运行 | `docker run -d` | 用于服务类应用 |
|
||||
| 启动已停止的容器 | `docker start` | 重用已有容器 |
|
||||
| 优雅停止 | `docker stop` | 先 SIGTERM,超时后 SIGKILL |
|
||||
| 强制停止 | `docker kill` | 直接 SIGKILL |
|
||||
| 重启 | `docker restart` | 停止后立即启动 |
|
||||
| 停止全部 | `docker stop $(docker ps -q)` | 停止所有运行中容器 |
|
||||
| 进入容器调试 | `docker exec -it 容器名 bash` | 推荐方式 |
|
||||
| 执行单条命令 | `docker exec 容器名 命令` | 不进入交互模式 |
|
||||
| 查看主进程输出 | `docker attach 容器名` | 慎用,退出可能停止容器 |
|
||||
| 删除已停止容器 | `docker rm 容器名` | 需先停止 |
|
||||
| 强制删除运行中容器 | `docker rm -f 容器名` | 直接删除 |
|
||||
| 删除容器及匿名卷 | `docker rm -v 容器名` | 同时清理匿名卷 |
|
||||
| 清理所有已停止容器 | `docker container prune` | 批量清理 |
|
||||
|
||||
### 延伸阅读
|
||||
|
||||
- [后台运行](5.2_daemon.md):理解 `-d` 参数和容器生命周期
|
||||
- [进入容器](5.4_attach_exec.md):操作运行中的容器
|
||||
- [网络配置](../09_network/README.md):理解端口映射的原理
|
||||
- [数据管理](../08_data/README.md):数据持久化方案
|
||||
- [删除镜像](../04_image/4.3_rm.md):清理镜像
|
||||
- [数据卷](../08_data/8.1_volume.md):数据卷管理
|
||||
---
|
||||
|
||||
> 📝 **发现错误或有改进建议?** 欢迎提交 [Issue](https://github.com/yeasy/docker_practice/issues) 或 [PR](https://github.com/yeasy/docker_practice/pulls)。
|
||||
136
06_repository/6.1_dockerhub.md
Normal file
136
06_repository/6.1_dockerhub.md
Normal file
@@ -0,0 +1,136 @@
|
||||
## 6.1 Docker Hub
|
||||
|
||||
### 6.1.1 什么是 Docker Hub
|
||||
|
||||
Docker Hub 是 Docker 的中央镜像仓库,通过它您可以轻松地分享和获取 Docker 镜像。
|
||||
|
||||
|
||||
[Docker Hub](https://hub.docker.com/) 是 Docker 官方维护的公共镜像仓库,也是全球最大的容器镜像库。
|
||||
|
||||
它提供了:
|
||||
|
||||
- **官方镜像**:由 Docker 官方和软件厂商 (如 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 会自动运行构建。这保证了镜像总是与代码同步,且由可信的官方环境构建。
|
||||
|
||||
---
|
||||
BIN
06_repository/6.2_registry.md
Normal file
BIN
06_repository/6.2_registry.md
Normal file
Binary file not shown.
217
06_repository/6.3_registry_auth.md
Normal file
217
06_repository/6.3_registry_auth.md
Normal file
@@ -0,0 +1,217 @@
|
||||
## 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/),这里不再赘述。
|
||||
BIN
06_repository/6.4_nexus3_registry.md
Normal file
BIN
06_repository/6.4_nexus3_registry.md
Normal file
Binary file not shown.
14
06_repository/README.md
Normal file
14
06_repository/README.md
Normal file
@@ -0,0 +1,14 @@
|
||||
# 第六章 访问仓库
|
||||
|
||||
仓库 (`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)
|
||||
2
06_repository/demo/auth/nginx.htpasswd
Normal file
2
06_repository/demo/auth/nginx.htpasswd
Normal file
@@ -0,0 +1,2 @@
|
||||
username:$2y$05$TRWvCC6ilpKpY3ICifw32Ok3.8SpG3etq8O5WGdCm9wvyDhtSbRgy
|
||||
|
||||
35
06_repository/demo/config.yml
Normal file
35
06_repository/demo/config.yml
Normal file
@@ -0,0 +1,35 @@
|
||||
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
|
||||
13
06_repository/demo/docker-compose.yml
Normal file
13
06_repository/demo/docker-compose.yml
Normal file
@@ -0,0 +1,13 @@
|
||||
|
||||
|
||||
services:
|
||||
registry:
|
||||
image: registry:2
|
||||
ports:
|
||||
- "443:5000"
|
||||
volumes:
|
||||
- ./:/etc/docker/registry
|
||||
- registry-data:/var/lib/registry
|
||||
|
||||
volumes:
|
||||
registry-data:
|
||||
4
06_repository/demo/root-ca.cnf
Normal file
4
06_repository/demo/root-ca.cnf
Normal file
@@ -0,0 +1,4 @@
|
||||
[root_ca]
|
||||
basicConstraints = critical,CA:TRUE,pathlen:1
|
||||
keyUsage = critical, nonRepudiation, cRLSign, keyCertSign
|
||||
subjectKeyIdentifier=hash
|
||||
7
06_repository/demo/site.cnf
Normal file
7
06_repository/demo/site.cnf
Normal file
@@ -0,0 +1,7 @@
|
||||
[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
|
||||
35
06_repository/demo/ssl/docker.domain.com.crt
Normal file
35
06_repository/demo/ssl/docker.domain.com.crt
Normal file
@@ -0,0 +1,35 @@
|
||||
-----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-----
|
||||
51
06_repository/demo/ssl/docker.domain.com.key
Normal file
51
06_repository/demo/ssl/docker.domain.com.key
Normal file
@@ -0,0 +1,51 @@
|
||||
-----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-----
|
||||
18
06_repository/summary.md
Normal file
18
06_repository/summary.md
Normal file
@@ -0,0 +1,18 @@
|
||||
## 本章小结
|
||||
|
||||
本章介绍了 Docker Hub 的使用、私有仓库的搭建以及 Nexus 3 等企业级方案。
|
||||
|
||||
| 功能 | 说明 |
|
||||
|------|------|
|
||||
| **官方镜像** | 优先使用的基础镜像 |
|
||||
| **拉取限制** | 匿名 100 次/6h,登录 200 次/6h |
|
||||
| **安全** | 推荐开启 2FA 并使用 Access Token |
|
||||
| **自动化** | 支持 Webhooks 和自动构建 |
|
||||
|
||||
### 延伸阅读
|
||||
|
||||
- [私有仓库](6.2_registry.md):搭建自己的 Registry
|
||||
- [镜像加速器](../03_install/3.9_mirror.md):加速下载
|
||||
---
|
||||
|
||||
> 📝 **发现错误或有改进建议?** 欢迎提交 [Issue](https://github.com/yeasy/docker_practice/issues) 或 [PR](https://github.com/yeasy/docker_practice/pulls)。
|
||||
190
07_dockerfile/7.10_workdir.md
Normal file
190
07_dockerfile/7.10_workdir.md
Normal file
@@ -0,0 +1,190 @@
|
||||
## 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
|
||||
```
|
||||
|
||||
---
|
||||
279
07_dockerfile/7.11_user.md
Normal file
279
07_dockerfile/7.11_user.md
Normal file
@@ -0,0 +1,279 @@
|
||||
## 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`
|
||||
|
||||
---
|
||||
196
07_dockerfile/7.12_healthcheck.md
Normal file
196
07_dockerfile/7.12_healthcheck.md
Normal file
@@ -0,0 +1,196 @@
|
||||
## 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. 只检查核心依赖
|
||||
|
||||
健康检查应主要关注 **当前服务** 是否可用,而不是检查其下游依赖 (数据库等)。下游依赖的检查应由应用逻辑处理。
|
||||
|
||||
---
|
||||
145
07_dockerfile/7.13_onbuild.md
Normal file
145
07_dockerfile/7.13_onbuild.md
Normal file
@@ -0,0 +1,145 @@
|
||||
## 7.13 ONBUILD 为他人作嫁衣裳
|
||||
|
||||
### 7.13.1 基本语法
|
||||
|
||||
```docker
|
||||
ONBUILD <其它指令>
|
||||
```
|
||||
|
||||
`ONBUILD` 是一个特殊的指令,它后面跟的是其它指令 (如 `RUN`,`COPY` 等),这些指令 **在当前镜像构建时不会执行**,只有当以当前镜像为基础镜像去构建下一级镜像时才会被执行。
|
||||
|
||||
---
|
||||
|
||||
### 7.13.2 为什么需要 ONBUILD
|
||||
|
||||
`ONBUILD` 主要用于制作 **语言栈基础镜像** 或 **框架基础镜像**。
|
||||
|
||||
#### 场景:维护 Node.js 项目
|
||||
|
||||
假设你有多个 Node.js 项目,它们的构建流程都一样:
|
||||
|
||||
1. 创建目录
|
||||
2. 复制 `package.json`
|
||||
3. 执行 `npm install`
|
||||
4. 复制源码
|
||||
5. 启动应用
|
||||
|
||||
如果不使用 `ONBUILD`,每个项目的 Dockerfile 都要重复这些步骤,且通过 `COPY` 复制文件时,基础镜像无法预知子项目的文件名。
|
||||
|
||||
#### 使用 ONBUILD 的解决方案
|
||||
|
||||
**基础镜像 (my-node-base)**:
|
||||
|
||||
```docker
|
||||
FROM node:20-alpine
|
||||
WORKDIR /app
|
||||
|
||||
## 这些指令将在子镜像构建时执行
|
||||
|
||||
ONBUILD COPY package*.json ./
|
||||
ONBUILD RUN npm install
|
||||
ONBUILD COPY . .
|
||||
|
||||
CMD ["npm", "start"]
|
||||
```
|
||||
|
||||
**子项目 Dockerfile**:
|
||||
|
||||
```docker
|
||||
FROM my-node-base
|
||||
## 只需要一行!
|
||||
|
||||
## 构建时会自动执行 COPY 和 RUN
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.13.3 执行机制
|
||||
|
||||
```bash
|
||||
基础镜像构建:
|
||||
Dockerfile (含 ONBUILD) ──build──> 基础镜像 (记录了 ONBUILD 触发器)
|
||||
(指令未执行)
|
||||
|
||||
子镜像构建:
|
||||
FROM 基础镜像 ──build──> 读取基础镜像触发器 ──> 执行触发器指令 ──> 继续执行子 Dockerfile
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.13.4 常见使用场景
|
||||
|
||||
#### 1. 自动处理依赖安装
|
||||
|
||||
```docker
|
||||
## Python 基础镜像
|
||||
|
||||
ONBUILD COPY requirements.txt ./
|
||||
ONBUILD RUN pip install -r requirements.txt
|
||||
```
|
||||
|
||||
#### 2. 自动编译代码
|
||||
|
||||
```docker
|
||||
## Go 基础镜像
|
||||
|
||||
ONBUILD COPY . .
|
||||
ONBUILD RUN go build -o app main.go
|
||||
```
|
||||
|
||||
#### 3. 处理静态资源
|
||||
|
||||
```docker
|
||||
## Nginx 静态网站基础镜像
|
||||
|
||||
ONBUILD COPY dist/ /usr/share/nginx/html/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.13.5 注意事项
|
||||
|
||||
#### 1. 继承性限制
|
||||
|
||||
`ONBUILD` 指令 **只会继承一次**。
|
||||
|
||||
- 镜像 A (含 ONBUILD)
|
||||
- 镜像 B (FROM A) -> 触发 ONBUILD
|
||||
- 镜像 C (FROM B) -> **不会** 再次触发 ONBUILD
|
||||
|
||||
#### 2. 构建上下文
|
||||
|
||||
子镜像构建时,`ONBUILD COPY . .` 中的 `.` 指的是 **子项目** 的构建上下文,而不是基础镜像的上下文。
|
||||
|
||||
#### 3. 不允许级联
|
||||
|
||||
`ONBUILD ONBUILD` 是非法的。你不能写 `ONBUILD ONBUILD COPY ...`。
|
||||
|
||||
#### 4. 可能会导致构建失败
|
||||
|
||||
由于 `ONBUILD` 实际上是在子镜像中执行指令,如果子项目的上下文不满足要求 (例如缺少 `package.json`),会导致子镜像构建失败,且错误信息可能比较隐晦。
|
||||
|
||||
---
|
||||
|
||||
### 7.13.6 最佳实践
|
||||
|
||||
#### 1. 命名规范
|
||||
|
||||
建议在镜像标签中添加 `-onbuild` 后缀,明确告知使用者该镜像包含触发器。
|
||||
|
||||
```bash
|
||||
node:20-onbuild
|
||||
python:3.12-onbuild
|
||||
```
|
||||
|
||||
#### 2. 避免执行耗时操作
|
||||
|
||||
尽量不要在 `ONBUILD` 中执行过于耗时或不确定的操作 (如更新系统软件),这会让子镜像构建变得缓慢且不可控。
|
||||
|
||||
#### 3. 清理工作
|
||||
|
||||
如果 `ONBUILD` 指令产生了临时文件,最好在同一个指令链中清理,或者提供机制让子镜像清理。
|
||||
|
||||
---
|
||||
144
07_dockerfile/7.14_label.md
Normal file
144
07_dockerfile/7.14_label.md
Normal file
@@ -0,0 +1,144 @@
|
||||
## 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")
|
||||
```
|
||||
|
||||
---
|
||||
138
07_dockerfile/7.15_shell.md
Normal file
138
07_dockerfile/7.15_shell.md
Normal file
@@ -0,0 +1,138 @@
|
||||
## 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,这会使构建过程难以理解和调试。尽量在头部定义一次即可。
|
||||
|
||||
---
|
||||
36
07_dockerfile/7.16_references.md
Normal file
36
07_dockerfile/7.16_references.md
Normal file
@@ -0,0 +1,36 @@
|
||||
## 7.16 参考文档
|
||||
|
||||
### 官方文档
|
||||
|
||||
* `Dockerfile` 官方参考手册:https://docs.docker.com/engine/reference/builder/
|
||||
|
||||
* `Dockerfile` 最佳实践指南:https://docs.docker.com/develop/develop-images/dockerfile_best-practices/
|
||||
|
||||
* `Docker` 官方镜像 `Dockerfile` 库:https://github.com/docker-library/docs
|
||||
|
||||
### 常用指令总结
|
||||
|
||||
Dockerfile 中的常用指令包括:
|
||||
|
||||
- **FROM**: 指定基础镜像,必须是第一条指令
|
||||
- **RUN**: 在镜像中执行命令,用于安装软件包等
|
||||
- **WORKDIR**: 设置工作目录
|
||||
- **COPY/ADD**: 复制文件到镜像中
|
||||
- **EXPOSE**: 声明容器监听的端口
|
||||
- **ENV**: 设置环境变量
|
||||
- **ENTRYPOINT**: 容器启动时的入口点
|
||||
- **CMD**: 容器默认执行的命令
|
||||
|
||||
### 最佳实践建议
|
||||
|
||||
1. 使用具体的基础镜像版本标签而非 latest
|
||||
2. 最小化镜像层数,合并 RUN 指令
|
||||
3. 使用 .dockerignore 文件排除不必要的文件
|
||||
4. 安装必要的软件包后清理缓存
|
||||
5. 使用多阶段构建减小最终镜像体积
|
||||
6. 避免以 root 身份运行容器应用
|
||||
|
||||
### 相关资源
|
||||
|
||||
- Docker 官方镜像库:https://hub.docker.com/
|
||||
- Docker 镜像构建最佳实践:https://docs.docker.com/build/building/best-practices/
|
||||
190
07_dockerfile/7.17_multistage_builds.md
Normal file
190
07_dockerfile/7.17_multistage_builds.md
Normal file
@@ -0,0 +1,190 @@
|
||||
## 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: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
|
||||
```
|
||||
|
||||
现在运行脚本即可构建镜像
|
||||
|
||||
```bash
|
||||
$ chmod +x build.sh
|
||||
|
||||
$ ./build.sh
|
||||
```
|
||||
|
||||
对比两种方式生成的镜像大小
|
||||
|
||||
```bash
|
||||
$ docker image ls
|
||||
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
go/helloworld 2 f7cf3465432c 22 seconds ago 6.47MB
|
||||
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
|
||||
```
|
||||
|
||||
### 7.17.3 使用多阶段构建
|
||||
|
||||
为解决以上问题,Docker v17.05 开始支持多阶段构建 (`multistage builds`)。使用多阶段构建我们就可以很容易解决前面提到的问题,并且只需要编写一个 `Dockerfile`:
|
||||
|
||||
例如,编写 `Dockerfile` 文件
|
||||
|
||||
```docker
|
||||
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"]
|
||||
```
|
||||
|
||||
构建镜像
|
||||
|
||||
```bash
|
||||
$ docker build -t go/helloworld:3 .
|
||||
```
|
||||
|
||||
对比三个镜像大小
|
||||
|
||||
```bash
|
||||
$ docker image ls
|
||||
|
||||
REPOSITORY TAG IMAGE ID CREATED SIZE
|
||||
go/helloworld 3 d6911ed9c846 7 seconds ago 6.47MB
|
||||
go/helloworld 2 f7cf3465432c 22 seconds ago 6.47MB
|
||||
go/helloworld 1 f55d3e16affc 2 minutes ago 295MB
|
||||
```
|
||||
|
||||
很明显使用多阶段构建的镜像体积小,同时也完美解决了上边提到的问题。
|
||||
|
||||
### 7.17.4 只构建某一阶段的镜像
|
||||
|
||||
我们可以使用 `as` 来为某一阶段命名,例如
|
||||
|
||||
```docker
|
||||
FROM golang:alpine as builder
|
||||
```
|
||||
|
||||
例如当我们只想构建 `builder` 阶段的镜像时,增加 `--target=builder` 参数即可
|
||||
|
||||
```bash
|
||||
$ docker build --target builder -t username/imagename:tag .
|
||||
```
|
||||
|
||||
### 7.17.5 构建时从其他镜像复制文件
|
||||
|
||||
上面例子中我们使用 `COPY --from=0 /go/src/github.com/go/helloworld/app .` 从上一阶段的镜像中复制文件,我们也可以复制任意镜像中的文件。
|
||||
|
||||
```docker
|
||||
COPY --from=nginx:latest /etc/nginx/nginx.conf /nginx.conf
|
||||
```
|
||||
237
07_dockerfile/7.18_multistage_builds_laravel.md
Normal file
237
07_dockerfile/7.18_multistage_builds_laravel.md
Normal file
@@ -0,0 +1,237 @@
|
||||
## 7.18 实战多阶段构建 Laravel 镜像
|
||||
|
||||
> 本节适用于 PHP 开发者阅读。`Laravel` 基于 8.x 版本,各个版本的文件结构可能会有差异,请根据实际自行修改。
|
||||
|
||||
### 7.18.1 准备
|
||||
|
||||
新建一个 `Laravel` 项目或在已有的 `Laravel` 项目根目录下新建 `Dockerfile` `.dockerignore` `laravel.conf` 文件。
|
||||
|
||||
在 `.dockerignore` 文件中写入以下内容。
|
||||
|
||||
```bash
|
||||
.idea/
|
||||
.git/
|
||||
|
||||
vendor/
|
||||
|
||||
node_modules/
|
||||
|
||||
public/js/
|
||||
public/css/
|
||||
public/mix-manifest.json
|
||||
|
||||
yarn-error.log
|
||||
|
||||
bootstrap/cache/*
|
||||
storage/
|
||||
|
||||
## 自行添加其他需要排除的文件,例如 .env.* 文件
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
在 `laravel.conf` 文件中写入 nginx 配置。
|
||||
|
||||
```nginx
|
||||
server {
|
||||
listen 80 default_server;
|
||||
root /app/laravel/public;
|
||||
index index.php index.html;
|
||||
|
||||
location / {
|
||||
try_files $uri $uri/ /index.php?$query_string;
|
||||
}
|
||||
|
||||
location ~ .*\.php(\/.*)*$ {
|
||||
fastcgi_pass laravel:9000;
|
||||
include fastcgi.conf;
|
||||
|
||||
# fastcgi_connect_timeout 300;
|
||||
|
||||
# fastcgi_send_timeout 300;
|
||||
|
||||
# fastcgi_read_timeout 300;
|
||||
|
||||
}
|
||||
}
|
||||
```
|
||||
|
||||
### 7.18.2 前端构建
|
||||
|
||||
第一阶段进行前端构建。
|
||||
|
||||
```docker
|
||||
FROM node:alpine as frontend
|
||||
|
||||
COPY package.json /app/
|
||||
|
||||
RUN set -x ; cd /app \
|
||||
&& npm install --registry=https://registry.npmmirror.com
|
||||
|
||||
COPY webpack.mix.js webpack.config.js tailwind.config.js /app/
|
||||
COPY resources/ /app/resources/
|
||||
|
||||
RUN set -x ; cd /app \
|
||||
&& touch artisan \
|
||||
&& mkdir -p public \
|
||||
&& npm run production
|
||||
```
|
||||
|
||||
### 7.18.3 安装 Composer 依赖
|
||||
|
||||
第二阶段安装 Composer 依赖。
|
||||
|
||||
```docker
|
||||
FROM composer as composer
|
||||
|
||||
COPY database/ /app/database/
|
||||
COPY composer.json composer.lock /app/
|
||||
|
||||
RUN set -x ; cd /app \
|
||||
&& composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ \
|
||||
&& composer install \
|
||||
--ignore-platform-reqs \
|
||||
--no-interaction \
|
||||
--no-plugins \
|
||||
--no-scripts \
|
||||
--prefer-dist
|
||||
```
|
||||
|
||||
### 7.18.4 整合以上阶段所生成的文件
|
||||
|
||||
第三阶段对以上阶段生成的文件进行整合。
|
||||
|
||||
```docker
|
||||
FROM php:7.4-fpm-alpine as laravel
|
||||
|
||||
ARG LARAVEL_PATH=/app/laravel
|
||||
|
||||
COPY --from=composer /app/vendor/ ${LARAVEL_PATH}/vendor/
|
||||
COPY . ${LARAVEL_PATH}
|
||||
COPY --from=frontend /app/public/js/ ${LARAVEL_PATH}/public/js/
|
||||
COPY --from=frontend /app/public/css/ ${LARAVEL_PATH}/public/css/
|
||||
COPY --from=frontend /app/public/mix-manifest.json ${LARAVEL_PATH}/public/mix-manifest.json
|
||||
|
||||
RUN set -x ; cd ${LARAVEL_PATH} \
|
||||
&& mkdir -p storage \
|
||||
&& mkdir -p storage/framework/cache \
|
||||
&& mkdir -p storage/framework/sessions \
|
||||
&& mkdir -p storage/framework/testing \
|
||||
&& mkdir -p storage/framework/views \
|
||||
&& mkdir -p storage/logs \
|
||||
&& chmod -R 777 storage \
|
||||
&& php artisan package:discover
|
||||
```
|
||||
|
||||
### 7.18.5 最后一个阶段构建 NGINX 镜像
|
||||
|
||||
```docker
|
||||
FROM nginx:alpine as nginx
|
||||
|
||||
ARG LARAVEL_PATH=/app/laravel
|
||||
|
||||
COPY laravel.conf /etc/nginx/conf.d/
|
||||
COPY --from=laravel ${LARAVEL_PATH}/public ${LARAVEL_PATH}/public
|
||||
```
|
||||
|
||||
### 7.18.6 构建 Laravel 及 Nginx 镜像
|
||||
|
||||
使用 `docker build` 命令构建镜像。
|
||||
|
||||
```bash
|
||||
$ docker build -t my/laravel --target=laravel .
|
||||
|
||||
$ docker build -t my/nginx --target=nginx .
|
||||
```
|
||||
|
||||
### 7.18.7 启动容器并测试
|
||||
|
||||
新建 Docker 网络
|
||||
|
||||
```bash
|
||||
$ docker network create laravel
|
||||
```
|
||||
|
||||
启动 laravel 容器,`--name=laravel` 参数设定的名字必须与 `nginx` 配置文件中的 `fastcgi_pass laravel:9000;` 一致
|
||||
|
||||
```bash
|
||||
$ docker run -dit --rm --name=laravel --network=laravel my/laravel
|
||||
```
|
||||
|
||||
启动 nginx 容器
|
||||
|
||||
```bash
|
||||
$ docker run -dit --rm --network=laravel -p 8080:80 my/nginx
|
||||
```
|
||||
|
||||
浏览器访问 `127.0.0.1:8080` 可以看到 Laravel 项目首页。
|
||||
|
||||
> 也许 Laravel 项目依赖其他外部服务,例如 redis、MySQL,请自行启动这些服务之后再进行测试,本小节不再赘述。
|
||||
|
||||
### 7.18.8 生产环境优化
|
||||
|
||||
本小节内容为了方便测试,将配置文件直接放到了镜像中,实际在使用时 **建议** 将配置文件作为 `config` 或 `secret` 挂载到容器中,请读者自行学习 `Kubernetes` 的相关内容。
|
||||
|
||||
由于篇幅所限本小节只是简单列出,更多内容可以参考 https://github.com/khs1994-docker/laravel-demo 项目。
|
||||
|
||||
### 7.18.9 附录
|
||||
|
||||
完整的 `Dockerfile` 文件如下。
|
||||
|
||||
```docker
|
||||
FROM node:alpine as frontend
|
||||
|
||||
COPY package.json /app/
|
||||
|
||||
RUN set -x ; cd /app \
|
||||
&& npm install --registry=https://registry.npmmirror.com
|
||||
|
||||
COPY webpack.mix.js webpack.config.js tailwind.config.js /app/
|
||||
COPY resources/ /app/resources/
|
||||
|
||||
RUN set -x ; cd /app \
|
||||
&& touch artisan \
|
||||
&& mkdir -p public \
|
||||
&& npm run production
|
||||
|
||||
FROM composer as composer
|
||||
|
||||
COPY database/ /app/database/
|
||||
COPY composer.json /app/
|
||||
|
||||
RUN set -x ; cd /app \
|
||||
&& composer config -g repo.packagist composer https://mirrors.aliyun.com/composer/ \
|
||||
&& composer install \
|
||||
--ignore-platform-reqs \
|
||||
--no-interaction \
|
||||
--no-plugins \
|
||||
--no-scripts \
|
||||
--prefer-dist
|
||||
|
||||
FROM php:7.4-fpm-alpine as laravel
|
||||
|
||||
ARG LARAVEL_PATH=/app/laravel
|
||||
|
||||
COPY --from=composer /app/vendor/ ${LARAVEL_PATH}/vendor/
|
||||
COPY . ${LARAVEL_PATH}
|
||||
COPY --from=frontend /app/public/js/ ${LARAVEL_PATH}/public/js/
|
||||
COPY --from=frontend /app/public/css/ ${LARAVEL_PATH}/public/css/
|
||||
COPY --from=frontend /app/public/mix-manifest.json ${LARAVEL_PATH}/public/mix-manifest.json
|
||||
|
||||
RUN set -x ; cd ${LARAVEL_PATH} \
|
||||
&& mkdir -p storage \
|
||||
&& mkdir -p storage/framework/cache \
|
||||
&& mkdir -p storage/framework/sessions \
|
||||
&& mkdir -p storage/framework/testing \
|
||||
&& mkdir -p storage/framework/views \
|
||||
&& mkdir -p storage/logs \
|
||||
&& chmod -R 777 storage \
|
||||
&& php artisan package:discover
|
||||
|
||||
FROM nginx:alpine as nginx
|
||||
|
||||
ARG LARAVEL_PATH=/app/laravel
|
||||
|
||||
COPY laravel.conf /etc/nginx/conf.d/
|
||||
COPY --from=laravel ${LARAVEL_PATH}/public ${LARAVEL_PATH}/public
|
||||
```
|
||||
161
07_dockerfile/7.1_run.md
Normal file
161
07_dockerfile/7.1_run.md
Normal file
@@ -0,0 +1,161 @@
|
||||
## 7.1 RUN 执行命令
|
||||
|
||||
### 7.1.1 基本语法
|
||||
|
||||
```docker
|
||||
RUN <command>
|
||||
RUN ["executable", "param1", "param2"]
|
||||
```
|
||||
|
||||
`RUN` 指令是 Dockerfile 中最常用的指令之一。它在 **当前镜像层** 之上创建一个新层,执行指定的命令,并提交结果。
|
||||
|
||||
---
|
||||
|
||||
### 7.1.2 两种格式对比
|
||||
|
||||
#### 1. Shell 格式
|
||||
|
||||
```docker
|
||||
RUN apt-get update
|
||||
```
|
||||
|
||||
- **特点**:默认通过 `/bin/sh -c` 执行。
|
||||
- **优势**:可以使用环境变量、管道、重定向等 Shell 特性。
|
||||
- **示例**:
|
||||
```docker
|
||||
RUN echo "Hello" > /test.txt
|
||||
```
|
||||
|
||||
#### 2. Exec 格式
|
||||
|
||||
```docker
|
||||
RUN ["apt-get", "update"]
|
||||
```
|
||||
|
||||
- **特点**:直接调用可执行文件,不经过 Shell。
|
||||
- **优势**:避免 Shell 字符串解析问题,适用于参数中包含特殊字符的情况。
|
||||
- **注意**:无法使用 `$VAR` 环境变量替换 (除非显式调用 shell)。
|
||||
|
||||
---
|
||||
|
||||
### 7.1.3 常见最佳实践
|
||||
|
||||
#### 1. 组合命令 (减少层数)
|
||||
|
||||
每一个 `RUN` 指令都会新建一层镜像。为了减少镜像体积和层数,应使用 `&&` 连接命令。
|
||||
|
||||
**❌ 糟糕的写法** (创建 3 层):
|
||||
|
||||
```docker
|
||||
RUN apt-get update
|
||||
RUN apt-get install -y nginx
|
||||
RUN rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
**✅ 推荐写法** (创建 1 层):
|
||||
|
||||
```docker
|
||||
RUN apt-get update && \
|
||||
apt-get install -y nginx && \
|
||||
rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
#### 2. 清理缓存
|
||||
|
||||
在安装完软件后,立即清除缓存,可以显著减小镜像体积。
|
||||
|
||||
- **Debian/Ubuntu**:
|
||||
```docker
|
||||
RUN apt-get update && apt-get install -y package-bar \
|
||||
&& rm -rf /var/lib/apt/lists/*
|
||||
```
|
||||
|
||||
- **Alpine**:
|
||||
```docker
|
||||
RUN apk add --no-cache package-bar
|
||||
```
|
||||
|
||||
#### 3. 使用 `set -e` 和 `pipefail`
|
||||
|
||||
默认情况下,管道命令 `cmd1 | cmd2` 只要 `cmd2` 成功,整个 `RUN` 就视为成功。
|
||||
|
||||
**❌ 隐蔽的错误**:
|
||||
|
||||
```docker
|
||||
## 如果下载失败,gzip 可能会报错,但如果不影响后续,构建可能继续
|
||||
|
||||
RUN wget http://error-url | gzip -d > file
|
||||
```
|
||||
|
||||
**✅ 推荐写法**:
|
||||
|
||||
```docker
|
||||
SHELL ["/bin/bash", "-o", "pipefail", "-c"]
|
||||
RUN wget http://url | gzip -d > file
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.1.4 常见问题
|
||||
|
||||
#### Q:为什么 `RUN cd /app` 不生效?
|
||||
|
||||
```docker
|
||||
RUN cd /app
|
||||
RUN touch hello.txt
|
||||
```
|
||||
|
||||
**结果**:`hello.txt` 会出现在根目录 `/`,而不是 `/app`。**原因**:每个 `RUN` 都在一个新的 Shell/容器环境中执行。`cd` 只影响当前 `RUN` 的环境。**解决**:使用 `WORKDIR` 指令。
|
||||
|
||||
```docker
|
||||
WORKDIR /app
|
||||
RUN touch hello.txt
|
||||
```
|
||||
|
||||
#### Q:环境变量不生效?
|
||||
|
||||
```docker
|
||||
RUN export MY_VAR=hello
|
||||
RUN echo $MY_VAR
|
||||
```
|
||||
|
||||
**结果**:输出为空。**原因**:同上,环境变量只在当前 `RUN` 有效。**解决**:使用 `ENV` 指令,或在同一行 `RUN` 中导出。
|
||||
|
||||
```docker
|
||||
ENV MY_VAR=hello
|
||||
RUN echo $MY_VAR
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.1.5 高级技巧
|
||||
|
||||
#### 1. 使用 BuildKit 的挂载缓存
|
||||
|
||||
BuildKit 支持在 `RUN` 指令中使用 `--mount` 挂载缓存,加速构建。
|
||||
|
||||
```docker
|
||||
## 缓存 apt 包
|
||||
|
||||
RUN --mount=type=cache,target=/var/cache/apt,sharing=locked \
|
||||
--mount=type=cache,target=/var/lib/apt,sharing=locked \
|
||||
apt-get update && apt-get install -y gcc
|
||||
```
|
||||
|
||||
```docker
|
||||
## 缓存 Go 模块
|
||||
|
||||
RUN --mount=type=cache,target=/go/pkg/mod \
|
||||
go build -o app
|
||||
```
|
||||
|
||||
#### 2. 挂载密钥
|
||||
|
||||
安全地使用 SSH 密钥或 Token,而不将其记录在镜像中。
|
||||
|
||||
```docker
|
||||
RUN --mount=type=secret,id=mysecret \
|
||||
cat /run/secrets/mysecret
|
||||
```
|
||||
|
||||
---
|
||||
274
07_dockerfile/7.2_copy.md
Normal file
274
07_dockerfile/7.2_copy.md
Normal file
@@ -0,0 +1,274 @@
|
||||
## 7.2 COPY 复制文件
|
||||
|
||||
### 7.2.1 基本语法
|
||||
|
||||
```docker
|
||||
COPY [选项] <源路径>... <目标路径>
|
||||
COPY [选项] ["<源路径1>", "<源路径2>", ... "<目标路径>"]
|
||||
```
|
||||
|
||||
`COPY` 指令将构建上下文中的文件或目录复制到镜像内。
|
||||
|
||||
---
|
||||
|
||||
### 7.2.2 基本用法
|
||||
|
||||
#### 复制单个文件
|
||||
|
||||
```docker
|
||||
## 复制文件到指定目录
|
||||
|
||||
COPY package.json /app/
|
||||
|
||||
## 复制文件并重命名
|
||||
|
||||
COPY config.json /app/settings.json
|
||||
```
|
||||
|
||||
#### 复制多个文件
|
||||
|
||||
```docker
|
||||
## 复制多个指定文件
|
||||
|
||||
COPY package.json package-lock.json /app/
|
||||
|
||||
## 使用通配符
|
||||
|
||||
COPY *.json /app/
|
||||
COPY src/*.js /app/src/
|
||||
```
|
||||
|
||||
#### 复制目录
|
||||
|
||||
```docker
|
||||
## 复制整个目录的内容(不是目录本身)
|
||||
|
||||
COPY src/ /app/src/
|
||||
```
|
||||
|
||||
> ⚠️ **注意**:复制目录时,复制的是目录的 **内容**,不包含目录本身。
|
||||
|
||||
```bash
|
||||
构建上下文: 镜像内:
|
||||
src/ /app/src/
|
||||
├── index.js → ├── index.js
|
||||
└── utils.js └── utils.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.2.3 通配符规则
|
||||
|
||||
COPY 支持 Go 的 `filepath.Match` 通配符规则:
|
||||
|
||||
| 通配符 | 说明 | 示例 |
|
||||
|--------|------|------|
|
||||
| `*` | 匹配任意字符序列 | `*.json` |
|
||||
| `?` | 匹配单个字符 | `config?.json` |
|
||||
| `[abc]` | 匹配括号内任一字符 | `[abc].txt` |
|
||||
| `[a-z]` | 匹配范围内字符 | `file[0-9].txt` |
|
||||
|
||||
```docker
|
||||
COPY hom* /mydir/ # home.txt, homework.md 等
|
||||
COPY hom?.txt /mydir/ # home.txt, homy.txt 等
|
||||
COPY app[0-9].js /app/ # app0.js ~ app9.js
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.2.4 目标路径
|
||||
|
||||
#### 绝对路径
|
||||
|
||||
```docker
|
||||
COPY app.js /usr/src/app/
|
||||
```
|
||||
|
||||
#### 相对路径 (基于 WORKDIR)
|
||||
|
||||
```docker
|
||||
WORKDIR /app
|
||||
COPY package.json ./ # 复制到 /app/package.json
|
||||
COPY src/ ./src/ # 复制到 /app/src/
|
||||
```
|
||||
|
||||
#### 自动创建目录
|
||||
|
||||
如果目标目录不存在,Docker 会自动创建:
|
||||
|
||||
```docker
|
||||
## /app/config/ 不存在也会自动创建
|
||||
|
||||
COPY settings.json /app/config/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.2.5 修改文件所有者
|
||||
|
||||
使用 `--chown` 选项设置文件的用户和组:
|
||||
|
||||
```docker
|
||||
## 使用用户名和组名
|
||||
|
||||
COPY --chown=node:node package.json /app/
|
||||
|
||||
## 使用 UID 和 GID
|
||||
|
||||
COPY --chown=1000:1000 . /app/
|
||||
|
||||
## 只指定用户
|
||||
|
||||
COPY --chown=node . /app/
|
||||
```
|
||||
|
||||
> 💡 结合 `USER` 指令使用,确保应用以非 root 用户运行。
|
||||
|
||||
---
|
||||
|
||||
### 7.2.6 保留文件元数据
|
||||
|
||||
COPY 会保留源文件的元数据:
|
||||
|
||||
- 读、写、执行权限
|
||||
- 修改时间
|
||||
|
||||
这对于脚本文件特别重要:
|
||||
|
||||
```docker
|
||||
## start.sh 的可执行权限会被保留
|
||||
|
||||
COPY start.sh /app/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.2.7 COPY vs ADD
|
||||
|
||||
| 特性 | COPY | ADD |
|
||||
|------|------|-----|
|
||||
| 复制本地文件 | ✅ | ✅ |
|
||||
| 自动解压 tar | ❌ | ✅ |
|
||||
| 支持 URL | ❌ | ✅ (不推荐)|
|
||||
| 推荐程度 | ✅ **推荐** | ⚠️ 特殊场景使用 |
|
||||
|
||||
```docker
|
||||
## 推荐:使用 COPY
|
||||
|
||||
COPY app.tar.gz /app/
|
||||
RUN tar -xzf /app/app.tar.gz
|
||||
|
||||
## ADD 会自动解压(行为不明显,不推荐)
|
||||
|
||||
ADD app.tar.gz /app/
|
||||
```
|
||||
|
||||
> 笔者建议:除非需要自动解压 tar 文件,否则始终使用 COPY。明确的行为比隐式的魔法更好。
|
||||
|
||||
---
|
||||
|
||||
### 7.2.8 多阶段构建中的 COPY
|
||||
|
||||
#### 从其他构建阶段复制
|
||||
|
||||
```docker
|
||||
## 构建阶段
|
||||
|
||||
FROM node:20 AS builder
|
||||
WORKDIR /app
|
||||
COPY package*.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
RUN npm run build
|
||||
|
||||
## 生产阶段
|
||||
|
||||
FROM nginx:alpine
|
||||
COPY --from=builder /app/dist /usr/share/nginx/html
|
||||
```
|
||||
|
||||
#### 使用 --link 优化缓存
|
||||
|
||||
```docker
|
||||
## 使用 --link 后,文件以独立层添加,不依赖前序指令
|
||||
|
||||
COPY --link --from=builder /app/dist /usr/share/nginx/html
|
||||
```
|
||||
|
||||
`--link` 的优势:
|
||||
|
||||
- 更高效利用构建缓存
|
||||
- 并行化构建过程
|
||||
- 加速多阶段构建
|
||||
|
||||
---
|
||||
|
||||
### 7.2.9 dockerignore
|
||||
|
||||
使用 `.dockerignore` 排除不需要复制的文件:
|
||||
|
||||
```gitignore
|
||||
## .dockerignore
|
||||
|
||||
node_modules
|
||||
.git
|
||||
.env
|
||||
*.log
|
||||
Dockerfile
|
||||
.dockerignore
|
||||
```
|
||||
|
||||
这可以:
|
||||
|
||||
- 减小构建上下文大小
|
||||
- 加速构建
|
||||
- 避免复制敏感文件
|
||||
|
||||
---
|
||||
|
||||
### 7.2.10 最佳实践
|
||||
|
||||
#### 1. 利用缓存,先复制依赖文件
|
||||
|
||||
```docker
|
||||
## ✅ 好:先复制依赖定义,再安装,最后复制代码
|
||||
|
||||
COPY package.json package-lock.json ./
|
||||
RUN npm install
|
||||
COPY . .
|
||||
|
||||
## ❌ 差:一次性复制所有文件,代码变更会导致重新 npm install
|
||||
|
||||
COPY . .
|
||||
RUN npm install
|
||||
```
|
||||
|
||||
#### 2. 使用 .dockerignore
|
||||
|
||||
```docker
|
||||
## 确保 node_modules 不被复制
|
||||
|
||||
COPY . .
|
||||
## .dockerignore 中应包含 node_modules
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
#### 3. 明确复制路径
|
||||
|
||||
```docker
|
||||
## ✅ 好:明确的路径
|
||||
|
||||
COPY src/ /app/src/
|
||||
COPY package.json /app/
|
||||
|
||||
## ❌ 差:过于宽泛
|
||||
|
||||
COPY . .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
> **🔥 踩坑实录**
|
||||
>
|
||||
> 某公司在优化 Node.js 应用的 Docker 镜像时,发现构建出来的镜像体积超过了 2GB,远远超过生产部署的需求。排查发现,Dockerfile 中使用了 `COPY . .` 把整个构建上下文复制进镜像,导致 `node_modules/`、`.git/` 目录和大量测试数据全部被打包进镜像。最初他们没有创建 `.dockerignore` 文件,默认会复制所有文件。解决方案是添加一个 `.dockerignore` 文件,排除这些不必要的目录,使镜像缩小到了 200MB。这个教训深刻地说明了:`.dockerignore` 和 `.gitignore` 一样重要,应该在项目初始化时就创建,而不是等到出现问题时才想起来。建议的标准做法是先复制 `package.json` 和 `package-lock.json` 安装依赖,再复制应用代码,同时在 `.dockerignore` 中明确列出 `node_modules`、`.git`、test 目录等。
|
||||
225
07_dockerfile/7.3_add.md
Normal file
225
07_dockerfile/7.3_add.md
Normal file
@@ -0,0 +1,225 @@
|
||||
## 7.3 ADD 更高级的复制文件
|
||||
|
||||
### 7.3.1 基本语法
|
||||
|
||||
```docker
|
||||
ADD [选项] <源路径>... <目标路径>
|
||||
ADD [选项] ["<源路径>", ... "<目标路径>"]
|
||||
```
|
||||
|
||||
`ADD` 在 `COPY` 基础上增加了两个功能:
|
||||
|
||||
1. 自动解压 tar 压缩包
|
||||
2. 支持从 URL 下载文件 (不推荐)
|
||||
|
||||
---
|
||||
|
||||
### 7.3.2 ADD vs COPY
|
||||
|
||||
| 特性 | COPY | ADD |
|
||||
|------|------|-----|
|
||||
| 复制本地文件 | ✅ | ✅ |
|
||||
| 自动解压 tar | ❌ | ✅ |
|
||||
| 支持 URL | ❌ | ✅ (不推荐)|
|
||||
| 行为可预测性 | ✅ 高 | ⚠️ 低 |
|
||||
| 推荐程度 | ✅ **优先使用** | 仅解压场景 |
|
||||
|
||||
> 笔者建议:除非需要自动解压 tar 文件,否则始终使用 COPY。明确的行为比隐式的魔法更好。
|
||||
|
||||
---
|
||||
|
||||
### 7.3.3 自动解压功能
|
||||
|
||||
#### 基本用法 (自动解压本地 tar)
|
||||
|
||||
```docker
|
||||
## 自动解压 tar.gz 到目标目录
|
||||
|
||||
ADD app.tar.gz /app/
|
||||
```
|
||||
|
||||
ADD 会识别并解压以下格式:
|
||||
|
||||
- `.tar`
|
||||
- `.tar.gz` / `.tgz`
|
||||
- `.tar.bz2` / `.tbz2`
|
||||
- `.tar.xz` / `.txz`
|
||||
|
||||
#### 实际应用
|
||||
|
||||
官方基础镜像通常使用 ADD 解压根文件系统:
|
||||
|
||||
```docker
|
||||
FROM scratch
|
||||
ADD ubuntu-noble-core-cloudimg-amd64-root.tar.gz /
|
||||
```
|
||||
|
||||
#### 解压过程
|
||||
|
||||
```bash
|
||||
ADD app.tar.gz /app/
|
||||
│
|
||||
├─ 识别 .tar.gz 格式
|
||||
├─ 自动解压
|
||||
└─ 内容放入 /app/
|
||||
|
||||
app.tar.gz 包含: /app/ 目录结果:
|
||||
├── src/ ├── src/
|
||||
│ └── main.py │ └── main.py
|
||||
└── config.json └── config.json
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.3.4 URL 下载功能 (不推荐)
|
||||
|
||||
#### 基本用法
|
||||
|
||||
```docker
|
||||
## 从 URL 下载文件
|
||||
|
||||
ADD https://example.com/app.zip /app/app.zip
|
||||
```
|
||||
|
||||
#### 为什么不推荐
|
||||
|
||||
| 问题 | 说明 |
|
||||
|------|------|
|
||||
| 权限固定 | 下载的文件权限为 600,通常需要额外 RUN 修改 |
|
||||
| 不会解压 | URL 下载的压缩包不会自动解压 |
|
||||
| 缓存问题 | URL 内容变化时不会重新下载 |
|
||||
| 层数增加 | 需要额外 RUN 清理 |
|
||||
|
||||
#### 推荐替代方案
|
||||
|
||||
```docker
|
||||
## ❌ 不推荐:使用 ADD 下载
|
||||
|
||||
ADD https://example.com/app.tar.gz /tmp/
|
||||
RUN tar -xzf /tmp/app.tar.gz -C /app && rm /tmp/app.tar.gz
|
||||
|
||||
## ✅ 推荐:使用 RUN + curl
|
||||
|
||||
RUN curl -fsSL https://example.com/app.tar.gz | tar -xz -C /app
|
||||
```
|
||||
|
||||
优势:
|
||||
|
||||
- 一条 RUN 完成下载、解压、清理
|
||||
- 减少镜像层数
|
||||
- 更清晰的构建意图
|
||||
|
||||
---
|
||||
|
||||
### 7.3.5 修改文件所有者
|
||||
|
||||
```docker
|
||||
ADD --chown=node:node app.tar.gz /app/
|
||||
ADD --chown=1000:1000 files/ /app/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.3.6 何时使用 ADD
|
||||
|
||||
#### ✅ 适合使用 ADD
|
||||
|
||||
```docker
|
||||
## 解压本地 tar 文件
|
||||
|
||||
FROM scratch
|
||||
ADD rootfs.tar.gz /
|
||||
|
||||
## 解压应用包
|
||||
|
||||
ADD dist.tar.gz /app/
|
||||
```
|
||||
|
||||
#### ❌ 不适合使用 ADD
|
||||
|
||||
```docker
|
||||
## 复制普通文件(用 COPY)
|
||||
|
||||
ADD package.json /app/ # ❌
|
||||
COPY package.json /app/ # ✅
|
||||
|
||||
## 下载文件(用 RUN + curl)
|
||||
|
||||
ADD https://example.com/file / # ❌
|
||||
RUN curl -fsSL ... -o /file # ✅
|
||||
|
||||
## 需要保留 tar 不解压(用 COPY)
|
||||
|
||||
ADD archive.tar.gz /archives/ # ❌ 会解压
|
||||
COPY archive.tar.gz /archives/ # ✅ 保持原样
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.3.7 缓存行为
|
||||
|
||||
ADD 可能导致构建缓存失效:
|
||||
|
||||
```docker
|
||||
## 如果 app.tar.gz 内容变化,此层及后续层都需重建
|
||||
|
||||
ADD app.tar.gz /app/
|
||||
RUN npm install
|
||||
```
|
||||
|
||||
**优化建议**:
|
||||
|
||||
```docker
|
||||
## 先复制依赖文件
|
||||
|
||||
COPY package*.json /app/
|
||||
RUN npm install
|
||||
|
||||
## 再添加应用代码
|
||||
|
||||
ADD app.tar.gz /app/
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.3.8 最佳实践
|
||||
|
||||
#### 1. 默认使用 COPY
|
||||
|
||||
```docker
|
||||
## ✅ 大多数场景使用 COPY
|
||||
|
||||
COPY . /app/
|
||||
```
|
||||
|
||||
#### 2. 仅在需要解压时使用 ADD
|
||||
|
||||
```docker
|
||||
## ✅ 自动解压场景
|
||||
|
||||
ADD app.tar.gz /app/
|
||||
```
|
||||
|
||||
#### 3. 不要用 ADD 下载文件
|
||||
|
||||
```docker
|
||||
## ❌ 避免
|
||||
|
||||
ADD https://example.com/file.tar.gz /tmp/
|
||||
|
||||
## ✅ 推荐
|
||||
|
||||
RUN curl -fsSL https://example.com/file.tar.gz | tar -xz -C /app
|
||||
```
|
||||
|
||||
#### 4. 解压后清理
|
||||
|
||||
```docker
|
||||
## 如果需要控制解压过程
|
||||
|
||||
COPY app.tar.gz /tmp/
|
||||
RUN tar -xzf /tmp/app.tar.gz -C /app && \
|
||||
rm /tmp/app.tar.gz
|
||||
```
|
||||
|
||||
---
|
||||
278
07_dockerfile/7.4_cmd.md
Normal file
278
07_dockerfile/7.4_cmd.md
Normal file
@@ -0,0 +1,278 @@
|
||||
## 7.4 CMD 容器启动命令
|
||||
|
||||
### 7.4.1 什么是 CMD
|
||||
|
||||
`CMD` 指令用于指定容器启动时默认执行的命令。它定义了容器的 “主进程”。
|
||||
|
||||
> **核心概念**:容器的生命周期 = 主进程的生命周期。CMD 指定的命令就是这个主进程。
|
||||
|
||||
---
|
||||
|
||||
### 7.4.2 语法格式
|
||||
|
||||
CMD 有三种格式:
|
||||
|
||||
| 格式 | 语法 | 推荐程度 |
|
||||
|------|------|---------|
|
||||
| **exec 格式**| `CMD [“可执行文件”, “参数1”, “参数2”]` | ✅**推荐** |
|
||||
| **shell 格式** | `CMD 命令 参数1 参数2` | ⚠️ 简单场景 |
|
||||
| **参数格式** | `CMD [“参数1”, “参数2”]` | 配合 ENTRYPOINT |
|
||||
|
||||
#### exec 格式 (推荐)
|
||||
|
||||
```docker
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
CMD ["python", "app.py"]
|
||||
CMD ["node", "server.js"]
|
||||
```
|
||||
|
||||
**优点**:
|
||||
|
||||
- 直接执行指定程序,是容器的 PID 1
|
||||
- 正确接收信号 (如 SIGTERM)
|
||||
- 无需 shell 解析
|
||||
|
||||
#### shell 格式
|
||||
|
||||
```docker
|
||||
CMD echo "Hello World"
|
||||
CMD nginx -g "daemon off;"
|
||||
```
|
||||
|
||||
**实际执行**:会被包装为 `sh -c`
|
||||
|
||||
```docker
|
||||
## 你写的
|
||||
|
||||
CMD echo $HOME
|
||||
|
||||
## 实际执行的
|
||||
|
||||
CMD ["sh", "-c", "echo $HOME"]
|
||||
```
|
||||
|
||||
**优点**:可以使用环境变量、管道等 shell 特性 **缺点**:主进程是 sh,信号无法正确传递给应用
|
||||
|
||||
---
|
||||
|
||||
### 7.4.3 exec 格式 vs shell 格式
|
||||
|
||||
| 特性 | exec 格式 | shell 格式 |
|
||||
|------|----------|-----------|
|
||||
| 主进程 | 指定的程序 | `/bin/sh` |
|
||||
| 信号传递 | ✅ 正确 | ❌ 无法传递 |
|
||||
| 环境变量 | ❌ 需要 shell 包装 | ✅ 自动解析 |
|
||||
| 推荐使用 | ✅ 大多数场景 | 需要 shell 特性时 |
|
||||
|
||||
#### 信号传递问题示例
|
||||
|
||||
```docker
|
||||
## ❌ shell 格式:docker stop 会超时
|
||||
|
||||
CMD node server.js
|
||||
## 实际是 sh -c "node server.js"
|
||||
|
||||
## SIGTERM 发给 sh,不会传递给 node
|
||||
|
||||
## ✅ exec 格式:docker stop 正常工作
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
## SIGTERM 直接发给 node
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.4.4 运行时覆盖 CMD
|
||||
|
||||
`docker run` 后的命令会覆盖 Dockerfile 中的 CMD:
|
||||
|
||||
```bash
|
||||
## ubuntu 默认 CMD 是 /bin/bash
|
||||
|
||||
$ docker run -it ubuntu # 进入 bash
|
||||
$ docker run ubuntu cat /etc/os-release # 覆盖为 cat 命令
|
||||
```
|
||||
|
||||
```bash
|
||||
Dockerfile: docker run 命令:
|
||||
CMD ["/bin/bash"] + cat /etc/os-release
|
||||
│ │
|
||||
└───────► 被覆盖 ◄───────┘
|
||||
↓
|
||||
执行: cat /etc/os-release
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.4.5 经典错误:容器立即退出
|
||||
|
||||
#### 错误示例
|
||||
|
||||
```docker
|
||||
## ❌ 容器启动后立即退出
|
||||
|
||||
CMD service nginx start
|
||||
```
|
||||
|
||||
#### 原因分析
|
||||
|
||||
```bash
|
||||
1. CMD service nginx start
|
||||
↓ 被转换为
|
||||
2. CMD ["sh", "-c", "service nginx start"]
|
||||
↓
|
||||
3. sh 启动,执行 service 命令
|
||||
↓
|
||||
4. service 命令将 nginx 放到后台
|
||||
↓
|
||||
5. service 命令结束,sh 退出
|
||||
↓
|
||||
6. 容器主进程(sh)退出 → 容器停止
|
||||
```
|
||||
|
||||
#### 正确做法
|
||||
|
||||
```docker
|
||||
## ✅ 让 nginx 在前台运行
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.4.6 CMD vs ENTRYPOINT
|
||||
|
||||
| 指令 | 用途 | 运行时行为 |
|
||||
|------|------|-----------|
|
||||
| **CMD**| 默认命令 | `docker run` 参数会 **覆盖** 它 |
|
||||
| **ENTRYPOINT**| 入口点 | `docker run` 参数会 **追加** 到它后面 |
|
||||
|
||||
#### 单独使用 CMD
|
||||
|
||||
```docker
|
||||
## Dockerfile
|
||||
|
||||
CMD ["curl", "-s", "http://example.com"]
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker run myimage # 执行默认命令
|
||||
$ docker run myimage curl -v ... # 完全覆盖
|
||||
```
|
||||
|
||||
#### 搭配 ENTRYPOINT
|
||||
|
||||
```docker
|
||||
## Dockerfile
|
||||
|
||||
ENTRYPOINT ["curl", "-s"]
|
||||
CMD ["http://example.com"]
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker run myimage # curl -s http://example.com
|
||||
$ docker run myimage http://other.com # curl -s http://other.com(参数覆盖)
|
||||
```
|
||||
|
||||
详见 [ENTRYPOINT 入口点](7.5_entrypoint.md)章节。
|
||||
|
||||
---
|
||||
|
||||
### 7.4.7 最佳实践
|
||||
|
||||
#### 1. 优先使用 exec 格式
|
||||
|
||||
```docker
|
||||
## ✅ 推荐
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
|
||||
## ⚠️ 仅在需要 shell 特性时使用
|
||||
|
||||
CMD ["sh", "-c", "echo $PATH && python app.py"]
|
||||
```
|
||||
|
||||
#### 2. 确保应用在前台运行
|
||||
|
||||
```docker
|
||||
## ✅ 前台运行
|
||||
|
||||
CMD ["nginx", "-g", "daemon off;"]
|
||||
CMD ["apache2ctl", "-D", "FOREGROUND"]
|
||||
CMD ["java", "-jar", "app.jar"]
|
||||
|
||||
## ❌ 不要使用后台服务命令
|
||||
|
||||
CMD service nginx start
|
||||
CMD systemctl start nginx
|
||||
```
|
||||
|
||||
#### 3. 使用双引号
|
||||
|
||||
```docker
|
||||
## ✅ 正确:双引号
|
||||
|
||||
CMD ["node", "server.js"]
|
||||
|
||||
## ❌ 错误:单引号(JSON 不支持)
|
||||
|
||||
CMD ['node', 'server.js']
|
||||
```
|
||||
|
||||
#### 4. 配合 ENTRYPOINT 使用
|
||||
|
||||
```docker
|
||||
## 用于可配置参数的场景
|
||||
|
||||
ENTRYPOINT ["python", "app.py"]
|
||||
CMD ["--port", "8080"]
|
||||
|
||||
## 运行时可以覆盖端口
|
||||
|
||||
$ docker run myapp --port 9000
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.4.8 常见问题
|
||||
|
||||
#### Q:CMD 可以写多个吗?
|
||||
|
||||
不可以。多个 CMD 只有最后一个生效:
|
||||
|
||||
```docker
|
||||
CMD ["echo", "first"]
|
||||
CMD ["echo", "second"] # 只有这个生效
|
||||
```
|
||||
|
||||
#### Q:如何在 CMD 中使用环境变量?
|
||||
|
||||
```docker
|
||||
## 方法1:使用 shell 格式
|
||||
|
||||
CMD echo "Port is $PORT"
|
||||
|
||||
## 方法2:显式使用 sh -c
|
||||
|
||||
CMD ["sh", "-c", "echo Port is $PORT"]
|
||||
```
|
||||
|
||||
#### Q:为什么我的容器不响应 Ctrl+C?
|
||||
|
||||
可能是使用了 shell 格式,信号被 sh 吃掉了:
|
||||
|
||||
```docker
|
||||
## ❌ 信号无法传递
|
||||
|
||||
CMD python app.py
|
||||
|
||||
## ✅ 信号正确传递
|
||||
|
||||
CMD ["python", "app.py"]
|
||||
```
|
||||
|
||||
---
|
||||
315
07_dockerfile/7.5_entrypoint.md
Normal file
315
07_dockerfile/7.5_entrypoint.md
Normal file
@@ -0,0 +1,315 @@
|
||||
## 7.5 ENTRYPOINT 入口点
|
||||
|
||||
### 7.5.1 什么是 ENTRYPOINT
|
||||
|
||||
`ENTRYPOINT` 指定容器启动时运行的入口程序。与 CMD 不同,ENTRYPOINT 定义的命令不会被 `docker run` 的参数覆盖,而是 **接收这些参数**。
|
||||
|
||||
> **核心作用**:让镜像像一个可执行程序一样使用,`docker run` 的参数作为这个程序的参数。
|
||||
|
||||
---
|
||||
|
||||
### 7.5.2 语法格式
|
||||
|
||||
| 格式 | 语法 | 推荐程度 |
|
||||
|------|------|---------|
|
||||
| **exec 格式**| `ENTRYPOINT [“可执行文件”, “参数1”]` | ✅**推荐** |
|
||||
| **shell 格式** | `ENTRYPOINT 命令 参数` | ⚠️ 不推荐 |
|
||||
|
||||
```docker
|
||||
## exec 格式(推荐)
|
||||
|
||||
ENTRYPOINT ["nginx", "-g", "daemon off;"]
|
||||
|
||||
## shell 格式(不推荐)
|
||||
|
||||
ENTRYPOINT nginx -g "daemon off;"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.5.3 ENTRYPOINT vs CMD
|
||||
|
||||
#### 核心区别
|
||||
|
||||
| 特性 | ENTRYPOINT | CMD |
|
||||
|------|------------|-----|
|
||||
| **定位** | 固定的入口程序 | 默认参数 |
|
||||
| **docker run 参数** | 追加为参数 | 完全覆盖 |
|
||||
| **覆盖方式** | `--entrypoint` | 直接指定命令 |
|
||||
| **适用场景** | 把镜像当命令用 | 提供默认行为 |
|
||||
|
||||
#### 行为对比
|
||||
|
||||
```docker
|
||||
## 只用 CMD
|
||||
|
||||
CMD ["curl", "-s", "http://example.com"]
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker run myimage # curl -s http://example.com
|
||||
$ docker run myimage -v # 执行 -v(错误!)
|
||||
$ docker run myimage curl -v ... # curl -v ...(完全替换)
|
||||
```
|
||||
|
||||
```docker
|
||||
## 只用 ENTRYPOINT
|
||||
|
||||
ENTRYPOINT ["curl", "-s"]
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker run myimage # curl -s(缺参数)
|
||||
$ docker run myimage http://example.com # curl -s http://example.com ✓
|
||||
```
|
||||
|
||||
```docker
|
||||
## ENTRYPOINT + CMD 组合(推荐)
|
||||
|
||||
ENTRYPOINT ["curl", "-s"]
|
||||
CMD ["http://example.com"]
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker run myimage # curl -s http://example.com(默认)
|
||||
$ docker run myimage http://other.com # curl -s http://other.com ✓
|
||||
$ docker run myimage -v http://other.com # curl -s -v http://other.com ✓
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.5.4 场景一:让镜像像命令一样使用
|
||||
|
||||
#### 需求 (启动前准备)
|
||||
|
||||
创建一个查询公网 IP 的 “命令” 镜像。
|
||||
|
||||
#### 使用 CMD 的问题
|
||||
|
||||
```docker
|
||||
FROM ubuntu:24.04
|
||||
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||
CMD ["curl", "-s", "http://myip.ipip.net"]
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker run myip # ✓ 正常工作
|
||||
当前 IP:61.148.226.66
|
||||
|
||||
$ docker run myip -i # ✗ 错误!
|
||||
exec: "-i": executable file not found
|
||||
## -i 替换了整个 CMD,被当作可执行文件
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
#### 使用 ENTRYPOINT 解决
|
||||
|
||||
```docker
|
||||
FROM ubuntu:24.04
|
||||
RUN apt-get update && apt-get install -y curl && rm -rf /var/lib/apt/lists/*
|
||||
ENTRYPOINT ["curl", "-s", "http://myip.ipip.net"]
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker run myip # ✓ 正常工作
|
||||
当前 IP:61.148.226.66
|
||||
|
||||
$ docker run myip -i # ✓ 添加 -i 参数
|
||||
HTTP/1.1 200 OK
|
||||
...
|
||||
当前 IP:61.148.226.66
|
||||
```
|
||||
|
||||
#### 交互图示
|
||||
|
||||
```bash
|
||||
ENTRYPOINT ["curl", "-s", "http://myip.ipip.net"]
|
||||
│
|
||||
docker run myip -i
|
||||
│
|
||||
▼
|
||||
curl -s http://myip.ipip.net -i
|
||||
└─────────────────────────────┘
|
||||
ENTRYPOINT + docker run 参数
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.5.5 场景二:启动前的准备工作
|
||||
|
||||
#### 需求
|
||||
|
||||
在启动主服务前执行初始化脚本 (如数据库迁移、权限设置)。
|
||||
|
||||
#### 实现方式
|
||||
|
||||
```docker
|
||||
FROM redis:7-alpine
|
||||
COPY docker-entrypoint.sh /usr/local/bin/
|
||||
ENTRYPOINT ["docker-entrypoint.sh"]
|
||||
CMD ["redis-server"]
|
||||
```
|
||||
|
||||
**docker-entrypoint.sh**:
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
set -e
|
||||
|
||||
## 准备工作
|
||||
|
||||
echo "Initializing..."
|
||||
|
||||
## 如果第一个参数是 redis-server,以 redis 用户运行
|
||||
|
||||
if [ "$1" = 'redis-server' ]; then
|
||||
chown -R redis:redis /data
|
||||
exec gosu redis "$@"
|
||||
fi
|
||||
|
||||
## 其他命令直接执行
|
||||
|
||||
exec "$@"
|
||||
```
|
||||
|
||||
#### 工作流程
|
||||
|
||||
```bash
|
||||
docker run redis docker run redis bash
|
||||
│ │
|
||||
▼ ▼
|
||||
docker-entrypoint.sh redis-server docker-entrypoint.sh bash
|
||||
│ │
|
||||
├─ 初始化 ├─ 初始化
|
||||
├─ chown -R redis:redis /data │
|
||||
└─ exec gosu redis redis-server └─ exec bash
|
||||
(以 redis 用户运行) (以 root 用户运行)
|
||||
```
|
||||
|
||||
#### 关键点
|
||||
|
||||
1. **exec “$@”**:用传入的参数替换当前进程,确保信号正确传递
|
||||
2. **条件判断**:根据 CMD 不同执行不同逻辑
|
||||
3. **用户切换**:使用 `gosu` 切换用户 (比 `su` 更适合容器)
|
||||
|
||||
---
|
||||
|
||||
### 7.5.6 场景三:带参数的应用
|
||||
|
||||
```docker
|
||||
FROM python:3.12-slim
|
||||
WORKDIR /app
|
||||
COPY . .
|
||||
RUN pip install -r requirements.txt
|
||||
|
||||
ENTRYPOINT ["python", "app.py"]
|
||||
CMD ["--host", "0.0.0.0", "--port", "8080"]
|
||||
```
|
||||
|
||||
```bash
|
||||
## 使用默认参数
|
||||
|
||||
$ docker run myapp
|
||||
## 执行: python app.py --host 0.0.0.0 --port 8080
|
||||
|
||||
## 覆盖参数
|
||||
|
||||
$ docker run myapp --host 0.0.0.0 --port 9000
|
||||
## 执行: python app.py --host 0.0.0.0 --port 9000
|
||||
|
||||
## 完全不同的参数
|
||||
|
||||
$ docker run myapp --help
|
||||
## 执行: python app.py --help
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.5.7 覆盖 ENTRYPOINT
|
||||
|
||||
使用 `--entrypoint` 参数覆盖:
|
||||
|
||||
```bash
|
||||
## 正常运行
|
||||
|
||||
$ docker run myimage
|
||||
|
||||
## 覆盖 ENTRYPOINT 进入 shell 调试
|
||||
|
||||
$ docker run --entrypoint /bin/sh myimage
|
||||
|
||||
## 覆盖 ENTRYPOINT 并传入参数
|
||||
|
||||
$ docker run --entrypoint /bin/cat myimage /etc/os-release
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.5.8 ENTRYPOINT 与 CMD 组合表
|
||||
|
||||
| ENTRYPOINT | CMD | 最终执行命令 |
|
||||
|------------|-----|-------------|
|
||||
| 无 | 无 | 无 (容器无法启动)|
|
||||
| 无 | `["cmd", "p1"]` | `cmd p1` |
|
||||
| `["ep", "p1"]` | 无 | `ep p1` |
|
||||
| `["ep", "p1"]` | `["cmd", "p2"]` | `ep p1 cmd p2` |
|
||||
| `ep p1` (shell)| `["cmd", "p2"]` | `/bin/sh -c "ep p1"` (CMD 被忽略)|
|
||||
|
||||
> ⚠️ **注意**:shell 格式的 ENTRYPOINT 会忽略 CMD!
|
||||
|
||||
---
|
||||
|
||||
### 7.5.9 最佳实践
|
||||
|
||||
#### 1. 使用 exec 格式
|
||||
|
||||
```docker
|
||||
## ✅ 推荐
|
||||
|
||||
ENTRYPOINT ["python", "app.py"]
|
||||
|
||||
## ❌ 避免 shell 格式
|
||||
|
||||
ENTRYPOINT python app.py
|
||||
```
|
||||
|
||||
#### 2. 提供有意义的默认参数
|
||||
|
||||
```docker
|
||||
ENTRYPOINT ["nginx"]
|
||||
CMD ["-g", "daemon off;"]
|
||||
```
|
||||
|
||||
#### 3. 入口脚本使用 exec
|
||||
|
||||
```bash
|
||||
#!/bin/sh
|
||||
## 准备工作...
|
||||
|
||||
## 使用 exec 替换当前进程
|
||||
|
||||
exec "$@"
|
||||
```
|
||||
|
||||
#### 4. 处理信号
|
||||
|
||||
确保 ENTRYPOINT 脚本能正确传递信号:
|
||||
|
||||
```bash
|
||||
#!/bin/bash
|
||||
trap 'kill -TERM $PID' TERM INT
|
||||
|
||||
## 启动应用
|
||||
|
||||
app "$@" &
|
||||
PID=$!
|
||||
|
||||
## 等待应用退出
|
||||
|
||||
wait $PID
|
||||
```
|
||||
|
||||
---
|
||||
257
07_dockerfile/7.6_env.md
Normal file
257
07_dockerfile/7.6_env.md
Normal file
@@ -0,0 +1,257 @@
|
||||
## 7.6 ENV 设置环境变量
|
||||
|
||||
### 7.6.1 基本语法
|
||||
|
||||
```docker
|
||||
## 格式一:单个变量
|
||||
|
||||
ENV <key> <value>
|
||||
|
||||
## 格式二:多个变量(推荐)
|
||||
|
||||
ENV <key1>=<value1> <key2>=<value2> ...
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.6.2 基本用法
|
||||
|
||||
#### 设置单个变量
|
||||
|
||||
```docker
|
||||
ENV NODE_VERSION 20.10.0
|
||||
ENV APP_ENV production
|
||||
```
|
||||
|
||||
#### 设置多个变量
|
||||
|
||||
```docker
|
||||
ENV NODE_VERSION=20.10.0 \
|
||||
APP_ENV=production \
|
||||
APP_NAME="My Application"
|
||||
```
|
||||
|
||||
> 💡 包含空格的值用双引号括起来。
|
||||
|
||||
---
|
||||
|
||||
### 7.6.3 环境变量的作用
|
||||
|
||||
#### 1. 后续指令中使用
|
||||
|
||||
```docker
|
||||
ENV NODE_VERSION=20.10.0
|
||||
|
||||
## 在 RUN 中使用
|
||||
|
||||
RUN curl -fsSL https://nodejs.org/dist/v${NODE_VERSION}/node-v${NODE_VERSION}-linux-x64.tar.xz \
|
||||
| tar -xJ -C /usr/local --strip-components=1
|
||||
|
||||
## 在 WORKDIR 中使用
|
||||
|
||||
ENV APP_HOME=/app
|
||||
WORKDIR $APP_HOME
|
||||
|
||||
## 在 COPY 中使用
|
||||
|
||||
COPY . $APP_HOME
|
||||
```
|
||||
|
||||
#### 2. 容器运行时使用
|
||||
|
||||
```docker
|
||||
ENV DATABASE_URL=postgres://localhost/mydb
|
||||
```
|
||||
|
||||
应用代码中可以读取:
|
||||
|
||||
```python
|
||||
import os
|
||||
db_url = os.environ.get('DATABASE_URL')
|
||||
```
|
||||
|
||||
```javascript
|
||||
const dbUrl = process.env.DATABASE_URL;
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.6.4 支持环境变量的指令
|
||||
|
||||
以下指令可以使用 `$变量名` 或 `${变量名}` 格式:
|
||||
|
||||
| 指令 | 示例 |
|
||||
|------|------|
|
||||
| `RUN` | `RUN echo $VERSION` |
|
||||
| `CMD` | `CMD ["sh", "-c", "echo $HOME"]` |
|
||||
| `ENTRYPOINT` | 同上 |
|
||||
| `COPY` | `COPY . $APP_HOME` |
|
||||
| `ADD` | `ADD app.tar.gz $APP_HOME` |
|
||||
| `WORKDIR` | `WORKDIR $APP_HOME` |
|
||||
| `EXPOSE` | `EXPOSE $PORT` |
|
||||
| `VOLUME` | `VOLUME $DATA_DIR` |
|
||||
| `USER` | `USER $USERNAME` |
|
||||
| `LABEL` | `LABEL version=$VERSION` |
|
||||
| `FROM` | `FROM node:$NODE_VERSION` |
|
||||
|
||||
---
|
||||
|
||||
### 7.6.5 运行时覆盖
|
||||
|
||||
使用 `-e` 或 `--env` 覆盖 Dockerfile 中定义的环境变量:
|
||||
|
||||
```bash
|
||||
## 覆盖单个变量
|
||||
|
||||
$ docker run -e APP_ENV=development myimage
|
||||
|
||||
## 覆盖多个变量
|
||||
|
||||
$ docker run -e APP_ENV=development -e DEBUG=true myimage
|
||||
|
||||
## 从环境变量文件读取
|
||||
|
||||
$ docker run --env-file .env myimage
|
||||
```
|
||||
|
||||
#### 。env 文件格式
|
||||
|
||||
```bash
|
||||
## .env
|
||||
|
||||
APP_ENV=development
|
||||
DEBUG=true
|
||||
DATABASE_URL=postgres://localhost/mydb
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.6.6 ENV vs ARG
|
||||
|
||||
| 特性 | ENV | ARG |
|
||||
|------|-----|-----|
|
||||
| **生效时间** | 构建时 + 运行时 | 仅构建时 |
|
||||
| **持久性** | 写入镜像,运行时可用 | 构建后消失 |
|
||||
| **覆盖方式** | `docker run -e` | `docker build --build-arg` |
|
||||
| **适用场景** | 应用配置 | 构建参数 (如版本号)|
|
||||
|
||||
#### 组合使用
|
||||
|
||||
```docker
|
||||
## ARG 接收构建时参数
|
||||
|
||||
ARG NODE_VERSION=20
|
||||
|
||||
## ENV 保存到运行时
|
||||
|
||||
ENV NODE_VERSION=$NODE_VERSION
|
||||
|
||||
## 后续指令使用
|
||||
|
||||
RUN curl -fsSL https://nodejs.org/dist/v${NODE_VERSION}/...
|
||||
```
|
||||
|
||||
```bash
|
||||
## 构建时指定版本
|
||||
|
||||
$ docker build --build-arg NODE_VERSION=18 -t myapp .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.6.7 最佳实践
|
||||
|
||||
#### 1. 统一管理版本号
|
||||
|
||||
```docker
|
||||
## ✅ 好:版本集中管理
|
||||
|
||||
ENV NGINX_VERSION=1.25.0 \
|
||||
NODE_VERSION=20.10.0 \
|
||||
PYTHON_VERSION=3.12.0
|
||||
|
||||
RUN apt-get install nginx=${NGINX_VERSION}
|
||||
|
||||
## ❌ 差:版本分散在各处
|
||||
|
||||
RUN apt-get install nginx=1.25.0
|
||||
```
|
||||
|
||||
#### 2. 不要存储敏感信息
|
||||
|
||||
```docker
|
||||
## ❌ 错误:密码写入镜像
|
||||
|
||||
ENV DB_PASSWORD=secret123
|
||||
|
||||
## ✅ 正确:运行时传入
|
||||
|
||||
## docker run -e DB_PASSWORD=xxx myimage
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
#### 3. 为应用提供合理默认值
|
||||
|
||||
```docker
|
||||
ENV APP_ENV=production \
|
||||
APP_PORT=8080 \
|
||||
LOG_LEVEL=info
|
||||
```
|
||||
|
||||
#### 4. 使用有意义的变量名
|
||||
|
||||
```docker
|
||||
## ✅ 好:清晰的命名
|
||||
|
||||
ENV REDIS_HOST=localhost \
|
||||
REDIS_PORT=6379
|
||||
|
||||
## ❌ 差:模糊的命名
|
||||
|
||||
ENV HOST=localhost \
|
||||
PORT=6379
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.6.8 常见问题
|
||||
|
||||
#### Q:环境变量在 CMD 中不展开
|
||||
|
||||
exec 格式不会自动展开环境变量:
|
||||
|
||||
```docker
|
||||
## ❌ 不会展开 $PORT
|
||||
|
||||
CMD ["python", "app.py", "--port", "$PORT"]
|
||||
|
||||
## ✅ 使用 shell 格式或显式调用 sh
|
||||
|
||||
CMD ["sh", "-c", "python app.py --port $PORT"]
|
||||
```
|
||||
|
||||
#### Q:如何查看容器的环境变量
|
||||
|
||||
```bash
|
||||
$ docker inspect mycontainer --format '{{json .Config.Env}}'
|
||||
$ docker exec mycontainer env
|
||||
```
|
||||
|
||||
#### Q:多行 ENV 还是多个 ENV
|
||||
|
||||
```docker
|
||||
## ✅ 推荐:减少层数
|
||||
|
||||
ENV VAR1=value1 \
|
||||
VAR2=value2 \
|
||||
VAR3=value3
|
||||
|
||||
## ⚠️ 多个 ENV 会创建多层
|
||||
|
||||
ENV VAR1=value1
|
||||
ENV VAR2=value2
|
||||
ENV VAR3=value3
|
||||
```
|
||||
|
||||
---
|
||||
242
07_dockerfile/7.7_arg.md
Normal file
242
07_dockerfile/7.7_arg.md
Normal file
@@ -0,0 +1,242 @@
|
||||
## 7.7 ARG 构建参数
|
||||
|
||||
### 7.7.1 基本语法
|
||||
|
||||
```docker
|
||||
ARG <参数名>[=<默认值>]
|
||||
```
|
||||
|
||||
`ARG` 指令定义构建时的变量,可以在 `docker build` 时通过 `--build-arg` 传入。
|
||||
|
||||
---
|
||||
|
||||
### 7.7.2 ARG vs ENV
|
||||
|
||||
| 特性 | ARG | ENV |
|
||||
|------|-----|-----|
|
||||
| **生效时间** | 仅构建时 | 构建时 + 运行时 |
|
||||
| **持久性** | 构建后消失 | 写入镜像 |
|
||||
| **覆盖方式** | `docker build --build-arg` | `docker run -e` |
|
||||
| **适用场景** | 构建参数 (版本号等)| 应用配置 |
|
||||
| **可见性** | `docker history` 可见 | `docker inspect` 可见 |
|
||||
|
||||
```dockerfile
|
||||
构建时 运行时
|
||||
├─ ARG VERSION=1.0 │ (ARG 已消失)
|
||||
├─ ENV APP_ENV=prod │ APP_ENV=prod(仍存在)
|
||||
└─ RUN echo $VERSION │
|
||||
```
|
||||
|
||||
> ⚠️ **安全提示**:不要用 ARG 传递密码等敏感信息,`docker history` 可以查看所有 ARG 值。
|
||||
|
||||
---
|
||||
|
||||
### 7.7.3 基本用法
|
||||
|
||||
#### 定义和使用
|
||||
|
||||
```docker
|
||||
## 定义有默认值的 ARG
|
||||
|
||||
ARG NODE_VERSION=20
|
||||
|
||||
## 使用 ARG
|
||||
|
||||
FROM node:${NODE_VERSION}-alpine
|
||||
RUN echo "Using Node.js $NODE_VERSION"
|
||||
```
|
||||
|
||||
#### 构建时覆盖
|
||||
|
||||
```bash
|
||||
## 使用默认值
|
||||
|
||||
$ docker build -t myapp .
|
||||
|
||||
## 覆盖默认值
|
||||
|
||||
$ docker build --build-arg NODE_VERSION=18 -t myapp .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.7.4 ARG 的作用域
|
||||
|
||||
#### FROM 之前的 ARG
|
||||
|
||||
```docker
|
||||
## FROM 之前的 ARG 只能用于 FROM 指令
|
||||
|
||||
ARG REGISTRY=docker.io
|
||||
ARG IMAGE_NAME=node
|
||||
|
||||
FROM ${REGISTRY}/${IMAGE_NAME}:20
|
||||
|
||||
## ❌ 这里无法使用上面的 ARG
|
||||
|
||||
RUN echo $REGISTRY # 输出空
|
||||
```
|
||||
|
||||
#### FROM 之后重新声明
|
||||
|
||||
```docker
|
||||
ARG NODE_VERSION=20
|
||||
|
||||
FROM node:${NODE_VERSION}-alpine
|
||||
|
||||
## 需要再次声明才能使用
|
||||
|
||||
ARG NODE_VERSION
|
||||
RUN echo "Node version: $NODE_VERSION"
|
||||
```
|
||||
|
||||
#### 多阶段构建中的 ARG
|
||||
|
||||
```docker
|
||||
ARG BASE_VERSION=alpine
|
||||
|
||||
FROM node:20-${BASE_VERSION} AS builder
|
||||
## 需要重新声明
|
||||
|
||||
ARG NODE_VERSION=20
|
||||
RUN echo "Building with Node $NODE_VERSION"
|
||||
|
||||
FROM node:20-${BASE_VERSION}
|
||||
## 每个阶段都需要重新声明
|
||||
|
||||
ARG NODE_VERSION=20
|
||||
RUN echo "Running with Node $NODE_VERSION"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.7.5 常见使用场景
|
||||
|
||||
#### 1. 控制基础镜像版本
|
||||
|
||||
```docker
|
||||
ARG ALPINE_VERSION=3.19
|
||||
FROM alpine:${ALPINE_VERSION}
|
||||
```
|
||||
|
||||
```bash
|
||||
$ docker build --build-arg ALPINE_VERSION=3.18 .
|
||||
```
|
||||
|
||||
#### 2. 设置软件版本
|
||||
|
||||
```docker
|
||||
ARG NGINX_VERSION=1.25.0
|
||||
|
||||
RUN curl -fsSL https://nginx.org/download/nginx-${NGINX_VERSION}.tar.gz | tar -xz
|
||||
```
|
||||
|
||||
#### 3. 配置构建环境
|
||||
|
||||
```docker
|
||||
ARG BUILD_ENV=production
|
||||
ARG ENABLE_DEBUG=false
|
||||
|
||||
RUN if [ "$ENABLE_DEBUG" = "true" ]; then \
|
||||
npm install --include=dev; \
|
||||
else \
|
||||
npm install --production; \
|
||||
fi
|
||||
```
|
||||
|
||||
#### 4. 配置私有仓库
|
||||
|
||||
```docker
|
||||
ARG NPM_TOKEN
|
||||
|
||||
RUN echo "//registry.npmjs.org/:_authToken=${NPM_TOKEN}" > ~/.npmrc && \
|
||||
npm install && \
|
||||
rm ~/.npmrc
|
||||
```
|
||||
|
||||
```bash
|
||||
## 构建时传入 token
|
||||
|
||||
$ docker build --build-arg NPM_TOKEN=xxx .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.7.6 将 ARG 传递给 ENV
|
||||
|
||||
如果需要在运行时使用 ARG 的值:
|
||||
|
||||
```docker
|
||||
ARG VERSION=1.0.0
|
||||
|
||||
## 将 ARG 传递给 ENV
|
||||
|
||||
ENV APP_VERSION=$VERSION
|
||||
|
||||
## 运行时可用
|
||||
|
||||
CMD echo "App version: $APP_VERSION"
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.7.7 预定义 ARG
|
||||
|
||||
Docker 提供了一些预定义的 ARG,无需声明即可使用:
|
||||
|
||||
| ARG | 说明 |
|
||||
|-----|------|
|
||||
| `HTTP_PROXY` | HTTP 代理 |
|
||||
| `HTTPS_PROXY` | HTTPS 代理 |
|
||||
| `NO_PROXY` | 不使用代理的地址 |
|
||||
| `FTP_PROXY` | FTP 代理 |
|
||||
|
||||
```bash
|
||||
## 构建时使用代理
|
||||
|
||||
$ docker build --build-arg HTTP_PROXY=http://proxy:8080 .
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.7.8 最佳实践
|
||||
|
||||
#### 1. 为 ARG 提供合理默认值
|
||||
|
||||
```docker
|
||||
## ✅ 好:有默认值
|
||||
|
||||
ARG NODE_VERSION=20
|
||||
|
||||
## ⚠️ 需要每次传入
|
||||
|
||||
ARG NODE_VERSION
|
||||
```
|
||||
|
||||
#### 2. 不要用 ARG 存储敏感信息
|
||||
|
||||
```docker
|
||||
## ❌ 错误:密码会被记录在镜像历史中
|
||||
|
||||
ARG DB_PASSWORD
|
||||
RUN echo "password=$DB_PASSWORD" > /app/.env
|
||||
|
||||
## ✅ 正确:使用 secrets 或运行时环境变量
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
#### 3. 使用 ARG 提高构建灵活性
|
||||
|
||||
```docker
|
||||
ARG BASE_IMAGE=python:3.12-slim
|
||||
FROM ${BASE_IMAGE}
|
||||
|
||||
## 可以构建不同基础镜像的版本
|
||||
|
||||
## docker build --build-arg BASE_IMAGE=python:3.11-alpine .
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
---
|
||||
257
07_dockerfile/7.8_volume.md
Normal file
257
07_dockerfile/7.8_volume.md
Normal file
@@ -0,0 +1,257 @@
|
||||
## 7.8 VOLUME 定义匿名卷
|
||||
|
||||
### 7.8.1 基本语法
|
||||
|
||||
```docker
|
||||
VOLUME ["/路径1", "/路径2"]
|
||||
VOLUME /路径
|
||||
```
|
||||
|
||||
`VOLUME` 指令创建挂载点,并标记为外部挂载的卷。
|
||||
|
||||
---
|
||||
|
||||
### 7.8.2 为什么使用 VOLUME
|
||||
|
||||
> **核心原则**:容器存储层应该保持无状态,任何运行时数据都应该存储在卷中。
|
||||
|
||||
```mermaid
|
||||
flowchart LR
|
||||
subgraph NoVolume ["没有 VOLUME:"]
|
||||
direction TB
|
||||
subgraph Container1 ["容器存储层"]
|
||||
direction TB
|
||||
Files["数据库文件 (问题)<br/>日志文件<br/>上传文件"]
|
||||
end
|
||||
Result1["容器删除 = 数据丢失"]
|
||||
Container1 ~~~ Result1
|
||||
end
|
||||
|
||||
subgraph UseVolume ["使用 VOLUME:"]
|
||||
direction TB
|
||||
Container2["容器存储层<br/>(只读/无状态)"]
|
||||
subgraph Volume ["数据卷"]
|
||||
Data["持久化数据 (安全)"]
|
||||
end
|
||||
Container2 --> Volume
|
||||
Result2["容器删除,数据保留"]
|
||||
Volume ~~~ Result2
|
||||
end
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.8.3 基本用法
|
||||
|
||||
#### 定义单个卷
|
||||
|
||||
```docker
|
||||
FROM mysql:8.0
|
||||
VOLUME /var/lib/mysql
|
||||
```
|
||||
|
||||
#### 定义多个卷
|
||||
|
||||
```docker
|
||||
FROM myapp
|
||||
VOLUME ["/data", "/logs", "/config"]
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.8.4 VOLUME 的行为
|
||||
|
||||
#### 1. 自动创建匿名卷
|
||||
|
||||
如果运行时未指定挂载,Docker 会自动创建匿名卷:
|
||||
|
||||
```bash
|
||||
$ docker run mysql:8.0
|
||||
$ docker volume ls
|
||||
DRIVER VOLUME NAME
|
||||
local a1b2c3d4e5f6... # 自动创建的匿名卷
|
||||
```
|
||||
|
||||
#### 2. 可被命名卷覆盖
|
||||
|
||||
```bash
|
||||
## 使用命名卷替代匿名卷
|
||||
|
||||
$ docker run -v mysql_data:/var/lib/mysql mysql:8.0
|
||||
```
|
||||
|
||||
#### 3. 可被 Bind Mount 覆盖
|
||||
|
||||
```bash
|
||||
## 使用宿主机目录替代
|
||||
|
||||
$ docker run -v /my/data:/var/lib/mysql mysql:8.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.8.5 VOLUME 在构建时的特殊行为
|
||||
|
||||
> ⚠️ **重要**:VOLUME 之后对该目录的修改会被丢弃!
|
||||
|
||||
```docker
|
||||
FROM ubuntu
|
||||
VOLUME /data
|
||||
|
||||
## ❌ 这个文件不会出现在镜像中!
|
||||
|
||||
RUN echo "hello" > /data/test.txt
|
||||
```
|
||||
|
||||
**原因**:在构建过程中,VOLUME 指令会为该目录创建一个临时的匿名卷。后续 RUN 指令对该目录的写入实际发生在这个临时卷中,而非镜像层。当该 RUN 指令结束后,临时卷被丢弃,因此写入的内容不会保存到最终镜像中。注意:这与容器运行时创建的匿名卷是不同的——运行时创建的卷会在容器生命周期内持续存在。
|
||||
|
||||
#### 正确做法
|
||||
|
||||
```docker
|
||||
FROM ubuntu
|
||||
|
||||
## ✅ 先写入文件
|
||||
|
||||
RUN mkdir -p /data && echo "hello" > /data/test.txt
|
||||
|
||||
## 再声明 VOLUME
|
||||
|
||||
VOLUME /data
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.8.6 常见使用场景
|
||||
|
||||
#### 数据库持久化
|
||||
|
||||
```docker
|
||||
FROM postgres:15
|
||||
VOLUME /var/lib/postgresql/data
|
||||
```
|
||||
|
||||
#### 日志目录
|
||||
|
||||
```docker
|
||||
FROM nginx
|
||||
VOLUME /var/log/nginx
|
||||
```
|
||||
|
||||
#### 上传文件目录
|
||||
|
||||
```docker
|
||||
FROM myapp
|
||||
VOLUME /app/uploads
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.8.7 查看 VOLUME 定义
|
||||
|
||||
```bash
|
||||
## 查看镜像定义的 VOLUME
|
||||
|
||||
$ docker inspect mysql:8.0 --format '{{json .Config.Volumes}}' | jq
|
||||
{
|
||||
"/var/lib/mysql": {}
|
||||
}
|
||||
|
||||
## 查看容器挂载的卷
|
||||
|
||||
$ docker inspect mycontainer --format '{{json .Mounts}}' | jq
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.8.8 VOLUME vs docker run -v
|
||||
|
||||
| 特性 | Dockerfile VOLUME | docker run -v |
|
||||
|------|-------------------|---------------|
|
||||
| **定义时机** | 镜像构建时 | 容器运行时 |
|
||||
| **默认行为** | 创建匿名卷 | 可指定命名卷或路径 |
|
||||
| **灵活性** | 低 (固定路径)| 高 (可任意指定)|
|
||||
| **适用场景** | 定义必须持久化的路径 | 灵活的数据管理 |
|
||||
|
||||
---
|
||||
|
||||
### 7.8.9 在 Compose 中
|
||||
|
||||
在 Compose 中配置如下:
|
||||
|
||||
```yaml
|
||||
services:
|
||||
db:
|
||||
image: postgres:15
|
||||
volumes:
|
||||
# 命名卷(推荐)
|
||||
|
||||
- postgres_data:/var/lib/postgresql/data
|
||||
# Bind Mount
|
||||
|
||||
- ./init.sql:/docker-entrypoint-initdb.d/init.sql
|
||||
|
||||
volumes:
|
||||
postgres_data: # 声明命名卷
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.8.10 安全注意事项
|
||||
|
||||
#### 匿名卷可能导致数据丢失
|
||||
|
||||
```bash
|
||||
## 使用 --rm 运行的容器,匿名卷会在容器删除时一起删除
|
||||
|
||||
$ docker run --rm mysql:8.0
|
||||
## 容器停止后,数据丢失!
|
||||
|
||||
...
|
||||
```
|
||||
|
||||
**解决**:始终使用命名卷
|
||||
|
||||
```bash
|
||||
$ docker run -v mysql_data:/var/lib/mysql mysql:8.0
|
||||
```
|
||||
|
||||
---
|
||||
|
||||
### 7.8.11 最佳实践
|
||||
|
||||
#### 1. 定义必须持久化的路径
|
||||
|
||||
```docker
|
||||
## 数据库必须使用卷
|
||||
|
||||
FROM postgres:15
|
||||
VOLUME /var/lib/postgresql/data
|
||||
```
|
||||
|
||||
#### 2. 不要在 VOLUME 后修改目录
|
||||
|
||||
```docker
|
||||
## ❌ 避免
|
||||
|
||||
VOLUME /app/data
|
||||
RUN cp init-data.json /app/data/
|
||||
|
||||
## ✅ 正确
|
||||
|
||||
RUN mkdir -p /app/data && cp init-data.json /app/data/
|
||||
VOLUME /app/data
|
||||
```
|
||||
|
||||
#### 3. 文档中说明 VOLUME 用途
|
||||
|
||||
```docker
|
||||
## 持久化用户上传的文件
|
||||
|
||||
VOLUME /app/uploads
|
||||
|
||||
## 持久化数据库数据
|
||||
|
||||
VOLUME /var/lib/mysql
|
||||
```
|
||||
|
||||
---
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user