:原理、實(shí)踐與生產(chǎn)環(huán)境應(yīng)用)
1. Docker 到底解決了什么問(wèn)題作為一名從物理機(jī)時(shí)代走過(guò)來(lái)的老運(yùn)維第一次接觸Docker時(shí)確實(shí)被它的設(shè)計(jì)哲學(xué)震撼到了。記得2014年那會(huì)兒我們還在用Shell腳本批量部署環(huán)境每次上線(xiàn)都要在十幾臺(tái)服務(wù)器上重復(fù)安裝依賴(lài)、配置環(huán)境變量。最痛苦的是測(cè)試環(huán)境和生產(chǎn)環(huán)境的不一致問(wèn)題 - 在我本地明明是好的這句話(huà)成了開(kāi)發(fā)人員的口頭禪。Docker本質(zhì)上解決的是應(yīng)用交付的標(biāo)準(zhǔn)化問(wèn)題。想象一下集裝箱對(duì)海運(yùn)業(yè)的革命無(wú)論里面裝的是汽車(chē)還是香蕉集裝箱的規(guī)格都是統(tǒng)一的。Docker容器就是軟件世界的集裝箱它將應(yīng)用及其所有依賴(lài)代碼、運(yùn)行時(shí)、系統(tǒng)工具、系統(tǒng)庫(kù)打包成一個(gè)標(biāo)準(zhǔn)化的單元。我特別喜歡這個(gè)類(lèi)比物理機(jī)時(shí)代就像散裝運(yùn)輸每艘船服務(wù)器的貨物擺放方式都不同虛擬機(jī)時(shí)代給每批貨物配了專(zhuān)屬貨艙VM但貨艙本身很重容器時(shí)代標(biāo)準(zhǔn)集裝箱容器可以在任何貨輪主機(jī)上即插即用具體來(lái)說(shuō)Docker主要解決了以下痛點(diǎn)環(huán)境一致性問(wèn)題我們團(tuán)隊(duì)曾經(jīng)因?yàn)殚_(kāi)發(fā)機(jī)用的是OpenJDK 8而生產(chǎn)環(huán)境是Oracle JDK 7導(dǎo)致GC行為差異引發(fā)線(xiàn)上事故?,F(xiàn)在用Docker鏡像固化環(huán)境從開(kāi)發(fā)到生產(chǎn)完全一致。資源利用率問(wèn)題以前用虛擬機(jī)部署單體應(yīng)用CPU利用率經(jīng)常不到10%?,F(xiàn)在同一臺(tái)物理機(jī)通過(guò)容器部署微服務(wù)資源利用率提升3-5倍是常態(tài)。快速部署與擴(kuò)展電商大促時(shí)我們通過(guò)容器編排工具能在5分鐘內(nèi)擴(kuò)容200個(gè)節(jié)點(diǎn)這在虛擬機(jī)時(shí)代需要提前一周準(zhǔn)備。版本控制與回滾每個(gè)鏡像都有唯一tag出現(xiàn)問(wèn)題可以立即回滾到上個(gè)版本再也不用擔(dān)心依賴(lài)地獄。2. 鏡像與容器的本質(zhì)區(qū)別新手最容易混淆的概念就是鏡像Image和容器Container。讓我用做菜來(lái)類(lèi)比鏡像就像菜譜食材包包含完整的做菜指南啟動(dòng)命令和所有原料應(yīng)用代碼、依賴(lài)庫(kù)容器是按菜譜做出來(lái)的菜品每次執(zhí)行docker run就像按菜譜做一次菜2.1 鏡像的層次化結(jié)構(gòu)鏡像采用分層存儲(chǔ)設(shè)計(jì)這是Docker的精妙之處。當(dāng)我構(gòu)建一個(gè)Python應(yīng)用鏡像時(shí)FROM python:3.8-slim # 基礎(chǔ)層 - 就像操作系統(tǒng)安裝盤(pán) WORKDIR /app # 元數(shù)據(jù)層 - 記錄配置信息 COPY . . # 文件層 - 添加代碼 RUN pip install -r requirements.txt # 執(zhí)行層 - 安裝依賴(lài) CMD [python, app.py] # 啟動(dòng)指令層每行指令都會(huì)創(chuàng)建一個(gè)新層。這種設(shè)計(jì)帶來(lái)三大優(yōu)勢(shì)存儲(chǔ)高效多個(gè)鏡像可以共享基礎(chǔ)層快速構(gòu)建只需重建變更的層版本追溯可以精確查看每層的內(nèi)容經(jīng)驗(yàn)提示構(gòu)建鏡像時(shí)把變動(dòng)頻率低的層放在前面能更好利用緩存。比如先安裝依賴(lài)再拷貝代碼。2.2 容器的運(yùn)行時(shí)特性容器實(shí)質(zhì)上是鏡像的運(yùn)行實(shí)例但有幾個(gè)關(guān)鍵特性寫(xiě)時(shí)復(fù)制CoW容器層是鏡像之上的可寫(xiě)層所有修改都發(fā)生在這里。這解釋了為什么刪除容器后改動(dòng)會(huì)丟失。隔離性通過(guò)Linux的namespace實(shí)現(xiàn)資源隔離cgroups實(shí)現(xiàn)資源限制。但注意容器與宿主機(jī)共享內(nèi)核這與虛擬機(jī)有本質(zhì)不同。臨時(shí)性容器設(shè)計(jì)上是臨時(shí)性的重要數(shù)據(jù)應(yīng)該通過(guò)volume持久化。我們?cè)?jīng)因?yàn)闆](méi)掛載volume導(dǎo)致數(shù)據(jù)庫(kù)容器重啟后數(shù)據(jù)丟失。3. 核心組件工作原理3.1 Docker架構(gòu)全景Docker采用客戶(hù)端-服務(wù)器架構(gòu)主要組件包括------------- REST API --------------- | Docker CLI | ------------ | Docker Daemon | ------------- --------------- | v ---------------- | Containers | | (runtime) | ---------------- | v ---------------- | Images | | (存儲(chǔ)層) | ----------------實(shí)際體驗(yàn)案例當(dāng)執(zhí)行docker run -it ubuntu bash時(shí)CLI將命令通過(guò)/var/run/docker.sock發(fā)送給daemonDaemon檢查本地是否有ubuntu鏡像沒(méi)有則從Registry拉取創(chuàng)建可寫(xiě)容器層分配文件系統(tǒng)、網(wǎng)絡(luò)接口執(zhí)行/bin/bash命令3.2 網(wǎng)絡(luò)模型詳解Docker的網(wǎng)絡(luò)設(shè)計(jì)經(jīng)常讓新手困惑。默認(rèn)創(chuàng)建的容器會(huì)有三種網(wǎng)絡(luò)bridge默認(rèn)網(wǎng)絡(luò)容器通過(guò)docker0虛擬網(wǎng)卡通信host直接使用宿主機(jī)網(wǎng)絡(luò)棧none無(wú)網(wǎng)絡(luò)連接我們生產(chǎn)環(huán)境常用自定義bridge網(wǎng)絡(luò)docker network create --driver bridge mynet docker run --networkmynet -d --namemysql mysql:5.7 docker run --networkmynet -it --link mysql centos curl mysql:3306避坑指南早期用--link連接容器會(huì)導(dǎo)致依賴(lài)關(guān)系混亂現(xiàn)在應(yīng)該用自定義網(wǎng)絡(luò)服務(wù)發(fā)現(xiàn)。4. 實(shí)戰(zhàn)操作全流程4.1 安裝與配置優(yōu)化以CentOS 7為例推薦安裝方式# 卸載舊版本 sudo yum remove docker* # 安裝依賴(lài) sudo yum install -y yum-utils device-mapper-persistent-data lvm2 # 配置阿里云鏡像源國(guó)內(nèi)加速 sudo yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo # 安裝社區(qū)版 sudo yum install -y docker-ce docker-ce-cli containerd.io # 啟動(dòng)并設(shè)置開(kāi)機(jī)自啟 sudo systemctl enable --now docker # 將當(dāng)前用戶(hù)加入docker組避免每次sudo sudo usermod -aG docker $USER newgrp docker # 刷新組權(quán)限關(guān)鍵配置優(yōu)化/etc/docker/daemon.json{ registry-mirrors: [https://你的ID.mirror.aliyuncs.com], log-driver: json-file, log-opts: { max-size: 100m, max-file: 3 }, data-root: /data/docker # 修改默認(rèn)存儲(chǔ)位置 }4.2 鏡像操作實(shí)戰(zhàn)拉取鏡像的完整過(guò)程# 搜索官方鏡像 docker search nginx # 拉取指定版本不指定tag默認(rèn)latest docker pull nginx:1.21-alpine # 查看鏡像分層 docker history nginx:1.21-alpine # 導(dǎo)出/導(dǎo)入鏡像適用于離線(xiàn)環(huán)境 docker save -o nginx.tar nginx:1.21-alpine docker load -i nginx.tar構(gòu)建自定義鏡像準(zhǔn)備一個(gè)簡(jiǎn)單的Python應(yīng)用# app.py from flask import Flask app Flask(__name__) app.route(/) def hello(): return Hello, Docker! if __name__ __main__: app.run(host0.0.0.0, port5000)Dockerfile:# 使用輕量級(jí)基礎(chǔ)鏡像 FROM python:3.9-slim # 設(shè)置工作目錄 WORKDIR /app # 先拷貝依賴(lài)文件利用緩存層 COPY requirements.txt . RUN pip install --no-cache-dir -r requirements.txt # 再拷貝代碼 COPY . . # 暴露端口 EXPOSE 5000 # 啟動(dòng)命令 CMD [python, app.py]構(gòu)建命令docker build -t myapp:v1 .4.3 容器生命周期管理完整操作流程# 啟動(dòng)容器-d后臺(tái)運(yùn)行-p端口映射-v卷掛載 docker run -d -p 5000:5000 -v /data:/app/data --name myapp myapp:v1 # 查看運(yùn)行狀態(tài) docker ps -a # 查看日志 docker logs -f myapp # 進(jìn)入容器shell docker exec -it myapp /bin/bash # 停止/啟動(dòng)/重啟 docker stop myapp docker start myapp docker restart myapp # 刪除容器必須先停止 docker rm myapp資源限制示例# 限制CPU和內(nèi)存 docker run -d --cpus1.5 --memory2g --name limited-app myapp:v1 # 查看資源使用 docker stats limited-app5. 常見(jiàn)問(wèn)題排坑指南5.1 鏡像下載慢問(wèn)題解決方案配置國(guó)內(nèi)鏡像源前文已展示使用代理服務(wù)器需符合公司政策通過(guò)第三方工具下載# 使用阿里云鏡像加速器 docker pull registry.cn-hangzhou.aliyuncs.com/library/nginx:latest5.2 容器時(shí)區(qū)問(wèn)題現(xiàn)象容器內(nèi)時(shí)間與宿主機(jī)不一致解決方法# 方案1構(gòu)建時(shí)設(shè)置推薦 FROM alpine RUN apk add --no-cache tzdata ENV TZAsia/Shanghai # 方案2啟動(dòng)時(shí)掛載 docker run -v /etc/localtime:/etc/localtime:ro ...5.3 存儲(chǔ)空間清理鏡像積累問(wèn)題# 查看磁盤(pán)使用 docker system df # 自動(dòng)清理無(wú)用對(duì)象 docker system prune # 強(qiáng)制刪除所有未使用鏡像 docker system prune -a5.4 權(quán)限問(wèn)題處理典型錯(cuò)誤docker: Got permission denied while trying to connect to the Docker daemon socket...解決方案將用戶(hù)加入docker組前文安裝部分已介紹臨時(shí)使用sudo修改socket權(quán)限不推薦6. 生產(chǎn)環(huán)境最佳實(shí)踐經(jīng)過(guò)多個(gè)項(xiàng)目的實(shí)戰(zhàn)檢驗(yàn)總結(jié)出以下經(jīng)驗(yàn)鏡像優(yōu)化原則使用Alpine等小型基礎(chǔ)鏡像多階段構(gòu)建分離編譯環(huán)境和運(yùn)行環(huán)境合并RUN指令減少層數(shù)清理不必要的緩存文件容器安全準(zhǔn)則不以root用戶(hù)運(yùn)行USER指令只讀掛載敏感目錄:ro定期更新基礎(chǔ)鏡像掃描鏡像漏洞使用docker scan編排工具選擇簡(jiǎn)單應(yīng)用docker-compose復(fù)雜系統(tǒng)Kubernetes云服務(wù)ECS/AKS/EKS監(jiān)控方案# 使用cAdvisorPrometheusGrafana組合 docker run -d \ --namecadvisor \ --volume/:/rootfs:ro \ --volume/var/run:/var/run:ro \ --volume/sys:/sys:ro \ --volume/var/lib/docker/:/var/lib/docker:ro \ --publish8080:8080 \ google/cadvisor:latest7. 學(xué)習(xí)路徑建議對(duì)于剛接觸Docker的開(kāi)發(fā)者我建議的學(xué)習(xí)順序基礎(chǔ)階段1-2周掌握鏡像/容器基本操作理解Dockerfile語(yǔ)法熟悉網(wǎng)絡(luò)和存儲(chǔ)配置進(jìn)階階段2-4周學(xué)習(xí)多階段構(gòu)建實(shí)踐CI/CD集成了解安全加固方法生產(chǎn)實(shí)踐持續(xù)參與真實(shí)項(xiàng)目部署學(xué)習(xí)編排工具Swarm/K8s建立監(jiān)控告警體系推薦的學(xué)習(xí)資源官方文檔永遠(yuǎn)是最新最全的《Docker Deep Dive》理論實(shí)踐Katacoda交互式實(shí)驗(yàn)室已遷移到Play with Docker