docker能干啥试想一个场景你开在windows上开发的一个web项目好巧不巧你是用python开发的然后部署到服务器的时候先是费劲的安装mysqlredisnginx这些中间件然后跑python项目发现死活跑不起来要不就提示少包要不就提示函数不存在咋回事呢使用docker之后以后安装mysql、redis、es、nginx这些中间件这些就只需要一条命令即可并且以后做的项目项目部署也只需要一两条命令即可大大方便了开发安装、部署流程能看到这个文档的我相信大家应该很焦虑啊网上说啥的都有什么docker不行了现在都是k8s了podman未来要取代docker等等我反正就一句话该学学该用用真的淘汰了就去拥抱新变化我之前也学了jQuery还用django写了前后端不分离的网站但是也不妨碍我现在又学了go用前后端分离的技术写网站什么是dockerdocker是一个用Go语言实现的开源项目可以让我们方便的创建和使用容器docker将程序以及程序所有的依赖都打包到docker container这样你的程序可以在任何环境都会有一致的表现我一直觉得没有docker各个中间件的安装将会变得非常困难比如安装mysql、es、nginx这些因为不同的系统安装流程还不太一样现在有了docker之后只需要写个命令只要我能跑起来你运行这个命令也就能跑起来和操作系统没关系docker安装本课程统一都使用linux/centos7系统安装docker并且在此系统上进行docker的使用# 删除之前的docker残留yum -y remove docker* yum install -y yum-utils yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum install docker-ce docker-ce-cli containerd.io -y # 启动dockersystemctl start docker # 开机启动systemctl enable docker不太建议在windows上使用docker一是windows上使用docker性能不太好二是有细微的差异比如host网络三是实际部署的系统都是linux系统yum用不了的记得换源cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo yum clean all yum makecachedocker设置代理从2024年开始中国境内就不能在通过docker换国内源下载镜像了这一点确实劝退了很多人如果不会魔法的同学可以使用腾讯云的服务器腾讯云有docker私有源可以下载镜像也可以买一个境外、国外的服务器也可以直接拉取docker镜像真要随心所欲的拉取镜像最好的方法还是学会魔法以及配置docker代理当然本课程只能教你如何配置docker代理创建 /etc/systemd/system/docker.service.d/http-proxy.conf 配置文件 [Service]EnvironmentHTTP_PROXYhttp://192.168.80.1:7890EnvironmentHTTPS_PROXYhttp://192.168.80.1:7890然后重新加载配置并重启服务: systemctl daemon-reload systemctl restart docker 然后检查加载的配置: systemctl show docker --property Environment记得代理软件要开启内网访问docker快速使用大家是怎么安装mysql的呢原生安装翻遍官网找安装文件又要配置密码也要配置访问策略太麻烦使用docker直接一条命令docker run -itd -p 3306:3306 --namemysql -e MYSQL_ROOT_PASSWORD123456 mysql:5.7然后用你的mysql客户端就可以连上你的mysql了爽死好了我相信docker应该勾起了你的学习兴趣了那么课程正式开始如果访问不了大概率是端口没开firewall-cmd --list-ports firewall-cmd --zonepublic --add-port80/tcp --permanent firewall-cmd --reloaddocker镜像学习docker从镜像开始上面我提到的mysql、redis等在dockerhub上就会有大佬或者官方把这些中间件制作成docker镜像我们只需要使用docker pull 镜像名 就可以下载到我们本地了镜像的基本操作# 拉取镜像docker pull image_name:tag # 不写tag默认是latest# 检索镜像docker search image_name:tag # 列表docker images # 删除镜像docker rmi image_iddocker容器可以理解为一个非常精简的操作系统在这个操作系统上只部署了你需要的服务也就是你对应的镜像比如nginx的镜像它运行的容器就表示这个操作系统里面只有nginx这个软件服务比如mysql的镜像它运行的容器就表示这个操作系统里面只有mysql这个软件服务容器是 Docker 最核心的概念本质是基于镜像运行的隔离进程—— 它复用宿主机内核通过 Namespace、Cgroups 等技术实现资源隔离网络、文件系统、进程和限制CPU、内存比虚拟机更轻量、启动更快。容器列表# 查看正在运行中的容器docker ps # 查看所有容器docker ps -a # 只获取容器iddocker ps -a -q # 指定输出的内容docker ps -a --format table {{.ID}}\t{{.Names}}运行容器docker run [选项] 镜像名:标签 [容器启动参数]选项--name表示为启动的容器起个名字这个名字在宿主机上唯一-t为docker分配一个伪终端并绑定到容器的标准输入上-i是让容器的标准输入保持打开状态-v目录映射实现数据的持久化冒号前面表示宿主机的目录后面是容器内目录。目录不存在会自动生成。-p端口映射宿主机端口:容器端口-e表示要设置环境变量-d表示要以分离模式也就是后台模式启动容器这样执行后会返回容器ID不会进入交互界面。如果想要进入交互界面需要-i 和-t参数。--restart表示容器重启策略--privileged表示是否使用特权模式设置–privilegedtrue提升系统执行权限。设置为true后容器内的root用户才是真正的root权限否则只是一个普通用户。容器运行以mysql为例# 后台运行 并且 暴露端口 -p 宿主机端口:容器内端口docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3307:3306 mysql:5.7 # 传递环境变量 配置数据库名docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 -e MYSQL_DATABASEblog mysql:5.7 # 数据挂载 重启mysql数据也不会丢失 -v 宿主机路径:容器内路径docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3306:3306 -v ./mysql_data:/var/lib/mysql mysql:5.7 # 限制 1G 内存、1 个 CPU 核心超出内存时容器自动终止docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3306:3306 --memory 1G --cpus 1 mysql:5.7 # 容器重启策略always总是重启除非手动停止docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 --restart always mysql:5.7 #on-failure[:max-retries]仅在容器退出码非 0 时重启可指定最大重试次数#always总是重启包括手动停止后重启 Docker 服务时#unless-stopped除非手动停止否则一直重启。容器操作# 停止容器docker stop docker_id # 启动容器docker start docker_id # 重启容器docker restart docker_id # 删除没有运行的容器docker rm docker_id # 删除运行中的容器docker rm -f docker_id # 停止所有容器docker stop $(docker ps -a -q)# 删除所有容器docker rm -f $(docker ps -a -q)查看日志# 查看这个容器的全部日志docker logs 容器名称 # 持续查看日志docker logs -f 容器名称 # 持续查看前10条日志docker logs -f -n 10 容器名称 # 持续查看前10条日志 老版本dockerdocker logs -f --tail10 容器名称进入容器容器本质是隔离的进程进入容器本质是在该隔离进程的 Namespace命名空间中启动一个新的终端进程如sh、bash从而可以查看容器内文件、执行命令、调试 Go 程序如查看进程、日志、修改配置。关键前提只有运行中的容器才能进入docker exec -it 容器名/容器ID 容器内执行的命令端口映射Docker 容器默认有独立的网络命名空间隔离的网络环境容器内的 mysql 服务监听的端口如 3306仅能在容器内访问。端口映射通过 Docker 的iptables规则将「宿主机的端口」与「容器内的端口」建立映射关系外部请求访问宿主机端口时会被转发到容器内对应的端口从而实现外部访问mysql 服务。类比容器是一个 “独立房间”mysql服务在房间内的3306 端口提供服务端口映射相当于在房间门上贴了一个 “门牌号宿主机端口 3306”外部访客按门牌号敲门会被引导到房间内的 3306 端口。# 宿主机 3306 端口 → 容器 3306 端口外部通过 http://宿主机IP:3306 访问docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3307:3306 mysql:5.7 # 宿主机随机端口 → 容器 3306 端口docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3306 mysql:5.7 # 仅 192.168.80.185 这个网卡的 3306 端口映射到容器 3306docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 192.168.80.185.100:3306:3306 mysql:5.7 # 映射 多个端口docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3307:3306 -p 3306:3306 mysql:5.7容器内的 服务必须监听0.0.0.0而非127.0.0.1否则仅容器内可访问外部无法通过端口映射访问核心避坑点。比如 go的web服务// 错误仅容器内可访问http.ListenAndServe(127.0.0.1:8080, nil) // 正确外部可通过端口映射访问http.ListenAndServe(:8080, nil) // 等价于 0.0.0.0:8080目录映射容器的文件系统是 “临时层”—— 基于镜像的只读层创建可写层容器删除后可写层的文件如日志、配置修改、数据库数据会丢失。目录映射也叫 “挂载”通过将「宿主机的目录 / 文件」与「容器内的目录 / 文件」建立关联实现数据持久化程序写入的日志、数据存储到宿主机容器删除后数据不丢失文件共享宿主机修改配置文件容器内实时生效无需重启容器避免镜像膨胀无需将大文件如日志、数据库打包到镜像中。Docker 支持两种目录映射方式绑定挂载Bind Mount和数据卷VolumeGo 场景中前者用于配置文件 / 日志后者用于重要数据如数据库。绑定挂载直接将宿主机的自定义目录 / 文件挂载到容器适合需要手动修改、查看的场景如配置文件、日志目录。docker run -v 宿主机路径:容器内路径[:权限] 镜像名 # ro只读容器内只能读取不能修改宿主机文件# rw读写默认容器内可修改宿主机文件。# 宿主机 ./mysql_data 目录 → 容器 /var/lib/mysql 目录读写权限docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -v ./mysql_data:/var/lib/mysql mysql:5.7 # 目录映射之后删除容器再执行运行命令数据依然还在# 宿主机 ./xxx.txt 文件 → 容器 /etc/mysql/xxx.txt 文件只读权限防止容器修改docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -v ./xxx.txt:/xxx.txt:ro mysql:5.7要注意直接用vi、vim修改文件很可能是不会同步的因为vi编辑是创建一个新文件然后把老文件删掉再把新文件替换的方式会打破docker的同步机制数据卷VolumeDocker 管理的持久化存储数据卷是 Docker 专门创建的持久化目录存储在宿主机/var/lib/docker/volumes/下由 Docker 自动管理无需手动维护宿主机路径适合存储重要数据核心优势跨平台兼容性好Docker 统一管理路径不受宿主机目录结构影响支持权限控制、数据卷备份 / 恢复可在多个容器间共享数据如多个服务共享同一个数据卷。# 创建名为 mysql_data 的数据卷docker volume create mysql_data # 数据卷 mysql_data → 容器 /var/lib/mysql 目录docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3306:3306 -v mysql_data:/var/lib/mysql mysql:5.7 # 查看所有数据卷docker volume ls # 查看数据卷详情如存储路径docker volume inspect mysql_data # 删除数据卷需先解绑所有容器docker volume rm mysql_data # 清理所有未使用的卷docker volume prune容器常见问题容器启动就报错先docker logs看日志如果日志有错误输出基本上就知道怎么去解决没有日志docker run … sh 重新跑这个容器肯定能跑起来跑起来进容器手动执行本来的启动命令看看是什么反应容器启动后容器里面挂载目录下的原有文件消失宿主机挂载目录为空会覆盖容器内目录的原有文件Docker 挂载规则宿主机目录优先级高于容器内目录宿主机挂载的文件变成了目录Docker 挂载的「源路径」如果在宿主机上不存在Docker 会默认创建一个「目录」而非「文件」—— 哪怕你在容器内的目标路径写的是文件也会被这个新建的目录覆盖最终容器内看到的就是目录而非预期的文件。镜像构建上面的docker镜像和docker容器学完之后可以覆盖你50%的docker应用场景了针对一些复杂的操作比如部署go项目、python这些自己的项目又或者在一些基础镜像的基础上安装一些软件那就得写Dockerfile文件了先看部署go项目的dockerfile示例# 注释指定基础镜像必填所有指令的起点FROMgolang:alpineASbuilder# 设置环境变量ENV CGO_ENABLED 0ENV GOPROXY https://goproxy.cn,directRUN sed -i s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g /etc/apk/repositories# 设置工作目录WORKDIR/build# 复制文件ADD go.mod .COPY go.sum .ADD main.go .# 执行命令RUN go build -o mainFROMalpineWORKDIR/app# 维护者信息LABEL maintainerfengfengxx.comLABEL version1.0LABEL descriptionA demo Docker image for go app# 元数据可通过 docker inspect 镜像名 便于镜像管理# 多阶段构建COPY --frombuilder /build/main /appRUN sed -i s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g /etc/apk/repositoriesRUN apk add bash# 容器启动时执行的命令必填仅最后一个生效CMD [./main]# 暴露端口仅声明不实际映射用于文档说明EXPOSE8080构建命令# 完整命令docker build -f Dockerfile -t demo-app:v1.0 . # 可省略-fdocker build -t demo-app:v1.0 .构建上下文docker build最后一个参数如.、/home/user/project指定的路径就是「构建上下文」。Docker 客户端会将该路径下的所有文件 / 目录打包发送给 Docker 守护进程daemon供 Dockerfile 中的COPY/ADD指令读取文件。需要额外了解的COPY和ADD将本地文件 / 目录复制到镜像的指定路径核心区别ADD支持自动解压压缩包、下载 URL 文件不推荐建议用RUN wget替代。# 复制本地 项目的所有文件到 到镜像的 /app 目录COPY . .# ADD 自动解压本地压缩包到镜像如 tar.gz、zipADD . /app/RUN、CMD和ENTRYPOINTrun在镜像构建过程中执行命令如安装依赖、解压文件、创建目录执行结果会被固化到镜像的分层中。两种格式# Shell 格式安装 nginx合并命令减少镜像分层RUN apt update apt install -y nginx rm -rf /var/lib/apt/lists/*# Exec 格式创建目录无 Shell 环境不支持变量替换RUN[mkdir, -p, /app/logs]cmd默认启动命令可被docker run后面的参数覆盖ENTRYPOINT入口命令docker run后面的参数会作为其参数追加不可覆盖除非用--entrypoint参数强制替换。# ENTRYPOINT固定启动命令ENTRYPOINT [./main]# CMD默认启动参数# 这里默认指定 --envdev 和 --port8080运行时可替换CMD [--env, dev, --port, 8080]docker网络Docker 网络是实现容器间、容器与外部宿主机 / 互联网通信的核心组件其本质是通过Linux 网络命名空间Network Namespace实现容器网络隔离再通过桥接、路由、端口映射等技术打通通信通道。安装 Docker 后会自动创建 3 个默认网络docker network ls NETWORK ID NAME DRIVER SCOPE abc123xyz bridge bridge local # 默认桥接网络def456uvw host host local # 主机模式网络ghi789rst none null local # 无网络模式桥接网络容器启动时Docker 自动分配独立 IP如 172.17.0.2、172.17.0.3子网默认 172.17.0.0/16网关宿主机 docker0 网桥 IP如 172.17.0.1容器间通过 IP 通信外部通过端口映射-p访问容器内的服务。局限性默认桥接网络不支持容器名解析只能通过 IP 通信所有默认桥接模式的容器在同一网段隔离性较差适合单服务不适合多服务集群。自建桥接网络手动创建的桥接网络相比默认 bridge 有 2 个核心优势支持容器名解析容器间可通过容器名作为主机名通信无需记 IP网络隔离不同自定义网络的容器无法通信安全性更高。# 创建名为 go-microservice 的自定义桥接网络docker network create go-microservice # 启动依赖容器Redis并加入网络docker run -d --name redis --network go-microservice redis:alpine # 另一个容器也加入这个网络docker run -d --name go-server --network go-microservice -p 8081:8080 go-demo:1.0 # 然后在这个容器里面就可以直接容器名访问对应的ip自定义容器网络网段和容器ip# 创建网段为 172.20.0.0/16、网关为 172.20.0.1 的自定义网络docker network create --subnet 172.20.0.0/16 --gateway 172.20.0.1 go-fixed-ip-network # 查看网络详情确认网段和网关docker network inspect go-fixed-ip-network # 指定固定 IP 172.18.0.10需在自定义网络子网内docker run -d --name go-server --network go-fixed-ip-network --ip 172.20.0.10 go-demo:1.0主机模式容器共享宿主机的网络命名空间意味着容器没有独立 IP直接使用宿主机的 IP容器内的端口直接暴露在宿主机上无需-p端口映射容器内的网络配置如路由、DNS与宿主机完全一致。局限性端口冲突风险容器内端口与宿主机 / 其他容器端口冲突会导致启动失败安全性较低容器直接暴露在宿主机网络无隔离性跨平台不兼容Windows/Mac 系统上 host 模式有兼容性限制推荐仅在 Linux 上使用。# 启动 Go 容器使用 host 模式监听宿主机 8080 端口docker run -d --name go-server --nethost go-demo:1.0 --port 8080docker composeDocker Compose 是 Docker 官方提供的多容器应用编排工具核心作用是通过一个统一的配置文件默认docker-compose.yml定义、管理多个关联的 Docker 容器实现「一键启动 / 停止 / 销毁」整个应用集群解决了手动逐个操作容器如网络配置、依赖顺序、端口映射的繁琐问题。当你的应用依赖多个服务时比如Web 服务 数据库 缓存 消息队列手动操作会面临以下痛点需逐个运行docker run命令参数冗长端口、网络、数据卷、环境变量等需手动维护容器间的网络连通如让 Web 容器访问 DB 容器容器启动顺序有依赖必须先启动 DB再启动 Web服务扩容、重启、日志查看需逐个操作。而 Docker Compose 可以用一个 YAML 文件声明所有服务配置一键启动 / 停止所有服务docker-compose up/down自动创建独立网络让容器间通过服务名互通维护容器依赖关系、数据卷持久化、环境变量隔离统一查看所有服务日志、扩容服务实例。基本结构# 版本声明推荐 v3兼容 Docker 17.06version: 3# 定义所有服务核心模块每个服务对应一个容器services: 服务1名称: # 镜像必填来自 Docker Hub 或自定义镜像image: 镜像名:标签# 容器名称可选默认自动生成container_name: 容器名# 端口映射可选主机端口:容器端口ports: - 主机端口1:容器端口1 - 主机端口2:容器端口2# 环境变量可选两种写法environment: - 键值 # 直接写 - 键2值2env_file: # 从文件读取优先级低于 environment - .env# 数据卷可选持久化数据或挂载主机目录volumes: - 主机目录:容器目录 # 绑定挂载 - 数据卷名称:容器目录 # 命名数据卷# 依赖服务可选启动顺序先启动依赖的服务depends_on: - 服务2名称 - 服务3名称# 网络可选指定服务加入的网络networks: - 自定义网络名# 重启策略可选容器退出后的重启规则restart: always # 可选no/on-failure/unless-stopped# 定义网络可选默认会创建一个默认网络networks: 自定义网络名: driver: bridge # 网络驱动默认 bridge支持 overlay 等# 定义数据卷可选用于持久化服务数据volumes: 数据卷名称: driver: local # 数据卷驱动默认 local示例# 版本声明推荐 v3兼容 Docker 17.06version: 3# 定义所有服务核心模块每个服务对应一个容器services: web: # 镜像必填来自 Docker Hub 或自定义镜像image: web:v1# 容器名称可选默认自动生成# container_name: 容器名# 端口映射可选主机端口:容器端口ports: - 8081:8080# 环境变量可选两种写法environment: - xxxdev # 直接写# 网络可选指定服务加入的网络networks: - xx_net# 重启策略可选容器退出后的重启规则restart: always # 可选no/on-failure/unless-stoppedweb1: image: web:v1ports: - 8082:8080networks: - xx_netrestart: always# 定义网络可选默认会创建一个默认网络networks: xx_net: driver: bridge # 网络驱动默认 bridge支持 overlay 等常用命令# 后台启动所有服务docker compose up -d # 查看 web 服务的实时日志docker compose logs -f web # 进入 db 服务容器如 MySQLdocker compose exec db bash # 停止所有服务保留容器和数据卷docker compose stop # 停止并删除所有服务、网络、容器保留数据卷docker compose down # 停止并删除所有包括数据卷docker compose down -v实战gomysql部署services: blog: image: web:v3networks: - blogrestart: alwaysvolumes: - ./settings.yaml:/app/settings.yamldepends_on: - mysqlmysql: image: mysql:5.7environment: - MYSQL_ROOT_PASSWORD123456 - MYSQL_DATABASEblogvolumes: - ./mysql_data:/var/lib/mysqlnetworks: - blogrestart: always# 定义网络可选默认会创建一个默认网络networks: blog: driver: bridge # 网络驱动默认 bridge支持 overlay 等go语言对接docker api有些情况下需要通过代码实现对容器的操作比如获取容器列表获取镜像列表创建容器删除容器packagemainimport ( contextfmtgithub.com/docker/docker/api/types/containergithub.com/docker/docker/api/types/imagegithub.com/docker/go-connections/natloggithub.com/docker/docker/client) varcli*client.Clientfuncinit() { // 初始化 Docker 客户端使用默认端点// 如需自定义端点如远程 Docker 服务可通过 client.WithHost(tcp://192.168.1.100:2375) 指定_cli, err:client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) iferr!nil { log.Fatalf(创建 Docker 客户端失败%v, err) } //defer cli.Close() // 退出时关闭客户端连接fmt.Println(Docker 客户端连接成功) cli _cli} funcimageList() { list, _:cli.ImageList(context.Background(), image.ListOptions{}) for_, summary:rangelist { fmt.Printf(%s %s\n, summary.ID[7:19], summary.RepoTags) } } funccontainerList() { list, _:cli.ContainerList(context.Background(), container.ListOptions{}) for_, summary:rangelist { fmt.Println(summary.ID[7:19], summary.Names, summary.State, summary.Image) } } //func (cli *Client) ContainerCreate(// ctx context.Context, // 上下文控制超时、取消等// config *container.Config, // 容器核心配置对应之前你问的 Config 结构体// hostConfig *container.HostConfig, // 主机资源配置与宿主机交互相关// networkingConfig *network.NetworkingConfig, // 网络配置容器网络栈、端口映射等// platform *ocispec.Platform, // 目标平台跨架构场景用如 arm64/amd64// containerName string // 容器名称可选如不指定则自动生成//) (container.CreateResponse, error)funccontainerCreate() { response, err:cli.ContainerCreate( context.Background(), container.Config{ AttachStdin: true, AttachStdout: true, OpenStdin: true, StdinOnce: true, Image: alpine:latest, Cmd: []string{sh}, }, container.HostConfig{ PortBindings: nat.PortMap{ 80: []nat.PortBinding{ { HostIP: 0.0.0.0, HostPort: 80, }, }, }, }, nil, nil, alpine, ) iferr!nil { fmt.Println(创建容器失败, err) return } fmt.Println(容器创建成功, response.ID) err cli.ContainerStart(context.Background(), response.ID, container.StartOptions{}) iferr!nil { fmt.Println(容器启动失败, err) return } fmt.Println(容器启动成功) } funcmain() { containerCreate() }docker自建仓库在内网环境下如果很多服务器都需要下载镜像每个都去配置docker代理会不太方便所以更多时候可以在一个服务器集中存镜像在那个服务器上运行docker registry服务创建一个docker镜像仓库其他服务器就可以直接拉取这个机器的镜像docker pull registry修改配置文件Docker 官方默认要求镜像仓库Registry必须使用HTTPS 协议通信保证传输加密和身份验证。但实际场景中很多企业 / 个人会搭建私有镜像仓库比如用 Docker 官方的registry镜像搭建这类私有仓库可能因成本 / 环境限制只开启 HTTP 协议未配置 HTTPS 证书。insecure-registries中文“不安全的仓库”告诉 Docker 「以下仓库是可信的允许用 HTTP 协议通信无需验证 HTTPS 证书」。vim /etc/docker/daemon.json {insecure-registries: [192.168.80.185:5000]}systemctl restart docker运行registry服务docker run -itd -v /data/registry:/var/lib/registry -p 5000:5000 --restartalways --name registry registry:latest上传镜像# 给镜像打tagdocker tag alpine:latest 192.168.80.185:5000/alpine:latest # 上传到私有仓库docker push 192.168.80.185:5000/alpine:latest拉取镜像要配置insecure-registriesdocker pull 192.168.80.185:5000/alpine
docker怎么学,一篇文档全教会你
发布时间:2026/6/11 8:02:36
docker能干啥试想一个场景你开在windows上开发的一个web项目好巧不巧你是用python开发的然后部署到服务器的时候先是费劲的安装mysqlredisnginx这些中间件然后跑python项目发现死活跑不起来要不就提示少包要不就提示函数不存在咋回事呢使用docker之后以后安装mysql、redis、es、nginx这些中间件这些就只需要一条命令即可并且以后做的项目项目部署也只需要一两条命令即可大大方便了开发安装、部署流程能看到这个文档的我相信大家应该很焦虑啊网上说啥的都有什么docker不行了现在都是k8s了podman未来要取代docker等等我反正就一句话该学学该用用真的淘汰了就去拥抱新变化我之前也学了jQuery还用django写了前后端不分离的网站但是也不妨碍我现在又学了go用前后端分离的技术写网站什么是dockerdocker是一个用Go语言实现的开源项目可以让我们方便的创建和使用容器docker将程序以及程序所有的依赖都打包到docker container这样你的程序可以在任何环境都会有一致的表现我一直觉得没有docker各个中间件的安装将会变得非常困难比如安装mysql、es、nginx这些因为不同的系统安装流程还不太一样现在有了docker之后只需要写个命令只要我能跑起来你运行这个命令也就能跑起来和操作系统没关系docker安装本课程统一都使用linux/centos7系统安装docker并且在此系统上进行docker的使用# 删除之前的docker残留yum -y remove docker* yum install -y yum-utils yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo yum install docker-ce docker-ce-cli containerd.io -y # 启动dockersystemctl start docker # 开机启动systemctl enable docker不太建议在windows上使用docker一是windows上使用docker性能不太好二是有细微的差异比如host网络三是实际部署的系统都是linux系统yum用不了的记得换源cp /etc/yum.repos.d/CentOS-Base.repo /etc/yum.repos.d/CentOS-Base.repo.backup wget -O /etc/yum.repos.d/CentOS-Base.repo http://mirrors.aliyun.com/repo/Centos-7.repo yum clean all yum makecachedocker设置代理从2024年开始中国境内就不能在通过docker换国内源下载镜像了这一点确实劝退了很多人如果不会魔法的同学可以使用腾讯云的服务器腾讯云有docker私有源可以下载镜像也可以买一个境外、国外的服务器也可以直接拉取docker镜像真要随心所欲的拉取镜像最好的方法还是学会魔法以及配置docker代理当然本课程只能教你如何配置docker代理创建 /etc/systemd/system/docker.service.d/http-proxy.conf 配置文件 [Service]EnvironmentHTTP_PROXYhttp://192.168.80.1:7890EnvironmentHTTPS_PROXYhttp://192.168.80.1:7890然后重新加载配置并重启服务: systemctl daemon-reload systemctl restart docker 然后检查加载的配置: systemctl show docker --property Environment记得代理软件要开启内网访问docker快速使用大家是怎么安装mysql的呢原生安装翻遍官网找安装文件又要配置密码也要配置访问策略太麻烦使用docker直接一条命令docker run -itd -p 3306:3306 --namemysql -e MYSQL_ROOT_PASSWORD123456 mysql:5.7然后用你的mysql客户端就可以连上你的mysql了爽死好了我相信docker应该勾起了你的学习兴趣了那么课程正式开始如果访问不了大概率是端口没开firewall-cmd --list-ports firewall-cmd --zonepublic --add-port80/tcp --permanent firewall-cmd --reloaddocker镜像学习docker从镜像开始上面我提到的mysql、redis等在dockerhub上就会有大佬或者官方把这些中间件制作成docker镜像我们只需要使用docker pull 镜像名 就可以下载到我们本地了镜像的基本操作# 拉取镜像docker pull image_name:tag # 不写tag默认是latest# 检索镜像docker search image_name:tag # 列表docker images # 删除镜像docker rmi image_iddocker容器可以理解为一个非常精简的操作系统在这个操作系统上只部署了你需要的服务也就是你对应的镜像比如nginx的镜像它运行的容器就表示这个操作系统里面只有nginx这个软件服务比如mysql的镜像它运行的容器就表示这个操作系统里面只有mysql这个软件服务容器是 Docker 最核心的概念本质是基于镜像运行的隔离进程—— 它复用宿主机内核通过 Namespace、Cgroups 等技术实现资源隔离网络、文件系统、进程和限制CPU、内存比虚拟机更轻量、启动更快。容器列表# 查看正在运行中的容器docker ps # 查看所有容器docker ps -a # 只获取容器iddocker ps -a -q # 指定输出的内容docker ps -a --format table {{.ID}}\t{{.Names}}运行容器docker run [选项] 镜像名:标签 [容器启动参数]选项--name表示为启动的容器起个名字这个名字在宿主机上唯一-t为docker分配一个伪终端并绑定到容器的标准输入上-i是让容器的标准输入保持打开状态-v目录映射实现数据的持久化冒号前面表示宿主机的目录后面是容器内目录。目录不存在会自动生成。-p端口映射宿主机端口:容器端口-e表示要设置环境变量-d表示要以分离模式也就是后台模式启动容器这样执行后会返回容器ID不会进入交互界面。如果想要进入交互界面需要-i 和-t参数。--restart表示容器重启策略--privileged表示是否使用特权模式设置–privilegedtrue提升系统执行权限。设置为true后容器内的root用户才是真正的root权限否则只是一个普通用户。容器运行以mysql为例# 后台运行 并且 暴露端口 -p 宿主机端口:容器内端口docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3307:3306 mysql:5.7 # 传递环境变量 配置数据库名docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD123456 -e MYSQL_DATABASEblog mysql:5.7 # 数据挂载 重启mysql数据也不会丢失 -v 宿主机路径:容器内路径docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3306:3306 -v ./mysql_data:/var/lib/mysql mysql:5.7 # 限制 1G 内存、1 个 CPU 核心超出内存时容器自动终止docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3306:3306 --memory 1G --cpus 1 mysql:5.7 # 容器重启策略always总是重启除非手动停止docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 --restart always mysql:5.7 #on-failure[:max-retries]仅在容器退出码非 0 时重启可指定最大重试次数#always总是重启包括手动停止后重启 Docker 服务时#unless-stopped除非手动停止否则一直重启。容器操作# 停止容器docker stop docker_id # 启动容器docker start docker_id # 重启容器docker restart docker_id # 删除没有运行的容器docker rm docker_id # 删除运行中的容器docker rm -f docker_id # 停止所有容器docker stop $(docker ps -a -q)# 删除所有容器docker rm -f $(docker ps -a -q)查看日志# 查看这个容器的全部日志docker logs 容器名称 # 持续查看日志docker logs -f 容器名称 # 持续查看前10条日志docker logs -f -n 10 容器名称 # 持续查看前10条日志 老版本dockerdocker logs -f --tail10 容器名称进入容器容器本质是隔离的进程进入容器本质是在该隔离进程的 Namespace命名空间中启动一个新的终端进程如sh、bash从而可以查看容器内文件、执行命令、调试 Go 程序如查看进程、日志、修改配置。关键前提只有运行中的容器才能进入docker exec -it 容器名/容器ID 容器内执行的命令端口映射Docker 容器默认有独立的网络命名空间隔离的网络环境容器内的 mysql 服务监听的端口如 3306仅能在容器内访问。端口映射通过 Docker 的iptables规则将「宿主机的端口」与「容器内的端口」建立映射关系外部请求访问宿主机端口时会被转发到容器内对应的端口从而实现外部访问mysql 服务。类比容器是一个 “独立房间”mysql服务在房间内的3306 端口提供服务端口映射相当于在房间门上贴了一个 “门牌号宿主机端口 3306”外部访客按门牌号敲门会被引导到房间内的 3306 端口。# 宿主机 3306 端口 → 容器 3306 端口外部通过 http://宿主机IP:3306 访问docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3307:3306 mysql:5.7 # 宿主机随机端口 → 容器 3306 端口docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3306 mysql:5.7 # 仅 192.168.80.185 这个网卡的 3306 端口映射到容器 3306docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 192.168.80.185.100:3306:3306 mysql:5.7 # 映射 多个端口docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3307:3306 -p 3306:3306 mysql:5.7容器内的 服务必须监听0.0.0.0而非127.0.0.1否则仅容器内可访问外部无法通过端口映射访问核心避坑点。比如 go的web服务// 错误仅容器内可访问http.ListenAndServe(127.0.0.1:8080, nil) // 正确外部可通过端口映射访问http.ListenAndServe(:8080, nil) // 等价于 0.0.0.0:8080目录映射容器的文件系统是 “临时层”—— 基于镜像的只读层创建可写层容器删除后可写层的文件如日志、配置修改、数据库数据会丢失。目录映射也叫 “挂载”通过将「宿主机的目录 / 文件」与「容器内的目录 / 文件」建立关联实现数据持久化程序写入的日志、数据存储到宿主机容器删除后数据不丢失文件共享宿主机修改配置文件容器内实时生效无需重启容器避免镜像膨胀无需将大文件如日志、数据库打包到镜像中。Docker 支持两种目录映射方式绑定挂载Bind Mount和数据卷VolumeGo 场景中前者用于配置文件 / 日志后者用于重要数据如数据库。绑定挂载直接将宿主机的自定义目录 / 文件挂载到容器适合需要手动修改、查看的场景如配置文件、日志目录。docker run -v 宿主机路径:容器内路径[:权限] 镜像名 # ro只读容器内只能读取不能修改宿主机文件# rw读写默认容器内可修改宿主机文件。# 宿主机 ./mysql_data 目录 → 容器 /var/lib/mysql 目录读写权限docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -v ./mysql_data:/var/lib/mysql mysql:5.7 # 目录映射之后删除容器再执行运行命令数据依然还在# 宿主机 ./xxx.txt 文件 → 容器 /etc/mysql/xxx.txt 文件只读权限防止容器修改docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -v ./xxx.txt:/xxx.txt:ro mysql:5.7要注意直接用vi、vim修改文件很可能是不会同步的因为vi编辑是创建一个新文件然后把老文件删掉再把新文件替换的方式会打破docker的同步机制数据卷VolumeDocker 管理的持久化存储数据卷是 Docker 专门创建的持久化目录存储在宿主机/var/lib/docker/volumes/下由 Docker 自动管理无需手动维护宿主机路径适合存储重要数据核心优势跨平台兼容性好Docker 统一管理路径不受宿主机目录结构影响支持权限控制、数据卷备份 / 恢复可在多个容器间共享数据如多个服务共享同一个数据卷。# 创建名为 mysql_data 的数据卷docker volume create mysql_data # 数据卷 mysql_data → 容器 /var/lib/mysql 目录docker run -d --name mysql -e MYSQL_ROOT_PASSWORD123456 -p 3306:3306 -v mysql_data:/var/lib/mysql mysql:5.7 # 查看所有数据卷docker volume ls # 查看数据卷详情如存储路径docker volume inspect mysql_data # 删除数据卷需先解绑所有容器docker volume rm mysql_data # 清理所有未使用的卷docker volume prune容器常见问题容器启动就报错先docker logs看日志如果日志有错误输出基本上就知道怎么去解决没有日志docker run … sh 重新跑这个容器肯定能跑起来跑起来进容器手动执行本来的启动命令看看是什么反应容器启动后容器里面挂载目录下的原有文件消失宿主机挂载目录为空会覆盖容器内目录的原有文件Docker 挂载规则宿主机目录优先级高于容器内目录宿主机挂载的文件变成了目录Docker 挂载的「源路径」如果在宿主机上不存在Docker 会默认创建一个「目录」而非「文件」—— 哪怕你在容器内的目标路径写的是文件也会被这个新建的目录覆盖最终容器内看到的就是目录而非预期的文件。镜像构建上面的docker镜像和docker容器学完之后可以覆盖你50%的docker应用场景了针对一些复杂的操作比如部署go项目、python这些自己的项目又或者在一些基础镜像的基础上安装一些软件那就得写Dockerfile文件了先看部署go项目的dockerfile示例# 注释指定基础镜像必填所有指令的起点FROMgolang:alpineASbuilder# 设置环境变量ENV CGO_ENABLED 0ENV GOPROXY https://goproxy.cn,directRUN sed -i s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g /etc/apk/repositories# 设置工作目录WORKDIR/build# 复制文件ADD go.mod .COPY go.sum .ADD main.go .# 执行命令RUN go build -o mainFROMalpineWORKDIR/app# 维护者信息LABEL maintainerfengfengxx.comLABEL version1.0LABEL descriptionA demo Docker image for go app# 元数据可通过 docker inspect 镜像名 便于镜像管理# 多阶段构建COPY --frombuilder /build/main /appRUN sed -i s/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g /etc/apk/repositoriesRUN apk add bash# 容器启动时执行的命令必填仅最后一个生效CMD [./main]# 暴露端口仅声明不实际映射用于文档说明EXPOSE8080构建命令# 完整命令docker build -f Dockerfile -t demo-app:v1.0 . # 可省略-fdocker build -t demo-app:v1.0 .构建上下文docker build最后一个参数如.、/home/user/project指定的路径就是「构建上下文」。Docker 客户端会将该路径下的所有文件 / 目录打包发送给 Docker 守护进程daemon供 Dockerfile 中的COPY/ADD指令读取文件。需要额外了解的COPY和ADD将本地文件 / 目录复制到镜像的指定路径核心区别ADD支持自动解压压缩包、下载 URL 文件不推荐建议用RUN wget替代。# 复制本地 项目的所有文件到 到镜像的 /app 目录COPY . .# ADD 自动解压本地压缩包到镜像如 tar.gz、zipADD . /app/RUN、CMD和ENTRYPOINTrun在镜像构建过程中执行命令如安装依赖、解压文件、创建目录执行结果会被固化到镜像的分层中。两种格式# Shell 格式安装 nginx合并命令减少镜像分层RUN apt update apt install -y nginx rm -rf /var/lib/apt/lists/*# Exec 格式创建目录无 Shell 环境不支持变量替换RUN[mkdir, -p, /app/logs]cmd默认启动命令可被docker run后面的参数覆盖ENTRYPOINT入口命令docker run后面的参数会作为其参数追加不可覆盖除非用--entrypoint参数强制替换。# ENTRYPOINT固定启动命令ENTRYPOINT [./main]# CMD默认启动参数# 这里默认指定 --envdev 和 --port8080运行时可替换CMD [--env, dev, --port, 8080]docker网络Docker 网络是实现容器间、容器与外部宿主机 / 互联网通信的核心组件其本质是通过Linux 网络命名空间Network Namespace实现容器网络隔离再通过桥接、路由、端口映射等技术打通通信通道。安装 Docker 后会自动创建 3 个默认网络docker network ls NETWORK ID NAME DRIVER SCOPE abc123xyz bridge bridge local # 默认桥接网络def456uvw host host local # 主机模式网络ghi789rst none null local # 无网络模式桥接网络容器启动时Docker 自动分配独立 IP如 172.17.0.2、172.17.0.3子网默认 172.17.0.0/16网关宿主机 docker0 网桥 IP如 172.17.0.1容器间通过 IP 通信外部通过端口映射-p访问容器内的服务。局限性默认桥接网络不支持容器名解析只能通过 IP 通信所有默认桥接模式的容器在同一网段隔离性较差适合单服务不适合多服务集群。自建桥接网络手动创建的桥接网络相比默认 bridge 有 2 个核心优势支持容器名解析容器间可通过容器名作为主机名通信无需记 IP网络隔离不同自定义网络的容器无法通信安全性更高。# 创建名为 go-microservice 的自定义桥接网络docker network create go-microservice # 启动依赖容器Redis并加入网络docker run -d --name redis --network go-microservice redis:alpine # 另一个容器也加入这个网络docker run -d --name go-server --network go-microservice -p 8081:8080 go-demo:1.0 # 然后在这个容器里面就可以直接容器名访问对应的ip自定义容器网络网段和容器ip# 创建网段为 172.20.0.0/16、网关为 172.20.0.1 的自定义网络docker network create --subnet 172.20.0.0/16 --gateway 172.20.0.1 go-fixed-ip-network # 查看网络详情确认网段和网关docker network inspect go-fixed-ip-network # 指定固定 IP 172.18.0.10需在自定义网络子网内docker run -d --name go-server --network go-fixed-ip-network --ip 172.20.0.10 go-demo:1.0主机模式容器共享宿主机的网络命名空间意味着容器没有独立 IP直接使用宿主机的 IP容器内的端口直接暴露在宿主机上无需-p端口映射容器内的网络配置如路由、DNS与宿主机完全一致。局限性端口冲突风险容器内端口与宿主机 / 其他容器端口冲突会导致启动失败安全性较低容器直接暴露在宿主机网络无隔离性跨平台不兼容Windows/Mac 系统上 host 模式有兼容性限制推荐仅在 Linux 上使用。# 启动 Go 容器使用 host 模式监听宿主机 8080 端口docker run -d --name go-server --nethost go-demo:1.0 --port 8080docker composeDocker Compose 是 Docker 官方提供的多容器应用编排工具核心作用是通过一个统一的配置文件默认docker-compose.yml定义、管理多个关联的 Docker 容器实现「一键启动 / 停止 / 销毁」整个应用集群解决了手动逐个操作容器如网络配置、依赖顺序、端口映射的繁琐问题。当你的应用依赖多个服务时比如Web 服务 数据库 缓存 消息队列手动操作会面临以下痛点需逐个运行docker run命令参数冗长端口、网络、数据卷、环境变量等需手动维护容器间的网络连通如让 Web 容器访问 DB 容器容器启动顺序有依赖必须先启动 DB再启动 Web服务扩容、重启、日志查看需逐个操作。而 Docker Compose 可以用一个 YAML 文件声明所有服务配置一键启动 / 停止所有服务docker-compose up/down自动创建独立网络让容器间通过服务名互通维护容器依赖关系、数据卷持久化、环境变量隔离统一查看所有服务日志、扩容服务实例。基本结构# 版本声明推荐 v3兼容 Docker 17.06version: 3# 定义所有服务核心模块每个服务对应一个容器services: 服务1名称: # 镜像必填来自 Docker Hub 或自定义镜像image: 镜像名:标签# 容器名称可选默认自动生成container_name: 容器名# 端口映射可选主机端口:容器端口ports: - 主机端口1:容器端口1 - 主机端口2:容器端口2# 环境变量可选两种写法environment: - 键值 # 直接写 - 键2值2env_file: # 从文件读取优先级低于 environment - .env# 数据卷可选持久化数据或挂载主机目录volumes: - 主机目录:容器目录 # 绑定挂载 - 数据卷名称:容器目录 # 命名数据卷# 依赖服务可选启动顺序先启动依赖的服务depends_on: - 服务2名称 - 服务3名称# 网络可选指定服务加入的网络networks: - 自定义网络名# 重启策略可选容器退出后的重启规则restart: always # 可选no/on-failure/unless-stopped# 定义网络可选默认会创建一个默认网络networks: 自定义网络名: driver: bridge # 网络驱动默认 bridge支持 overlay 等# 定义数据卷可选用于持久化服务数据volumes: 数据卷名称: driver: local # 数据卷驱动默认 local示例# 版本声明推荐 v3兼容 Docker 17.06version: 3# 定义所有服务核心模块每个服务对应一个容器services: web: # 镜像必填来自 Docker Hub 或自定义镜像image: web:v1# 容器名称可选默认自动生成# container_name: 容器名# 端口映射可选主机端口:容器端口ports: - 8081:8080# 环境变量可选两种写法environment: - xxxdev # 直接写# 网络可选指定服务加入的网络networks: - xx_net# 重启策略可选容器退出后的重启规则restart: always # 可选no/on-failure/unless-stoppedweb1: image: web:v1ports: - 8082:8080networks: - xx_netrestart: always# 定义网络可选默认会创建一个默认网络networks: xx_net: driver: bridge # 网络驱动默认 bridge支持 overlay 等常用命令# 后台启动所有服务docker compose up -d # 查看 web 服务的实时日志docker compose logs -f web # 进入 db 服务容器如 MySQLdocker compose exec db bash # 停止所有服务保留容器和数据卷docker compose stop # 停止并删除所有服务、网络、容器保留数据卷docker compose down # 停止并删除所有包括数据卷docker compose down -v实战gomysql部署services: blog: image: web:v3networks: - blogrestart: alwaysvolumes: - ./settings.yaml:/app/settings.yamldepends_on: - mysqlmysql: image: mysql:5.7environment: - MYSQL_ROOT_PASSWORD123456 - MYSQL_DATABASEblogvolumes: - ./mysql_data:/var/lib/mysqlnetworks: - blogrestart: always# 定义网络可选默认会创建一个默认网络networks: blog: driver: bridge # 网络驱动默认 bridge支持 overlay 等go语言对接docker api有些情况下需要通过代码实现对容器的操作比如获取容器列表获取镜像列表创建容器删除容器packagemainimport ( contextfmtgithub.com/docker/docker/api/types/containergithub.com/docker/docker/api/types/imagegithub.com/docker/go-connections/natloggithub.com/docker/docker/client) varcli*client.Clientfuncinit() { // 初始化 Docker 客户端使用默认端点// 如需自定义端点如远程 Docker 服务可通过 client.WithHost(tcp://192.168.1.100:2375) 指定_cli, err:client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation()) iferr!nil { log.Fatalf(创建 Docker 客户端失败%v, err) } //defer cli.Close() // 退出时关闭客户端连接fmt.Println(Docker 客户端连接成功) cli _cli} funcimageList() { list, _:cli.ImageList(context.Background(), image.ListOptions{}) for_, summary:rangelist { fmt.Printf(%s %s\n, summary.ID[7:19], summary.RepoTags) } } funccontainerList() { list, _:cli.ContainerList(context.Background(), container.ListOptions{}) for_, summary:rangelist { fmt.Println(summary.ID[7:19], summary.Names, summary.State, summary.Image) } } //func (cli *Client) ContainerCreate(// ctx context.Context, // 上下文控制超时、取消等// config *container.Config, // 容器核心配置对应之前你问的 Config 结构体// hostConfig *container.HostConfig, // 主机资源配置与宿主机交互相关// networkingConfig *network.NetworkingConfig, // 网络配置容器网络栈、端口映射等// platform *ocispec.Platform, // 目标平台跨架构场景用如 arm64/amd64// containerName string // 容器名称可选如不指定则自动生成//) (container.CreateResponse, error)funccontainerCreate() { response, err:cli.ContainerCreate( context.Background(), container.Config{ AttachStdin: true, AttachStdout: true, OpenStdin: true, StdinOnce: true, Image: alpine:latest, Cmd: []string{sh}, }, container.HostConfig{ PortBindings: nat.PortMap{ 80: []nat.PortBinding{ { HostIP: 0.0.0.0, HostPort: 80, }, }, }, }, nil, nil, alpine, ) iferr!nil { fmt.Println(创建容器失败, err) return } fmt.Println(容器创建成功, response.ID) err cli.ContainerStart(context.Background(), response.ID, container.StartOptions{}) iferr!nil { fmt.Println(容器启动失败, err) return } fmt.Println(容器启动成功) } funcmain() { containerCreate() }docker自建仓库在内网环境下如果很多服务器都需要下载镜像每个都去配置docker代理会不太方便所以更多时候可以在一个服务器集中存镜像在那个服务器上运行docker registry服务创建一个docker镜像仓库其他服务器就可以直接拉取这个机器的镜像docker pull registry修改配置文件Docker 官方默认要求镜像仓库Registry必须使用HTTPS 协议通信保证传输加密和身份验证。但实际场景中很多企业 / 个人会搭建私有镜像仓库比如用 Docker 官方的registry镜像搭建这类私有仓库可能因成本 / 环境限制只开启 HTTP 协议未配置 HTTPS 证书。insecure-registries中文“不安全的仓库”告诉 Docker 「以下仓库是可信的允许用 HTTP 协议通信无需验证 HTTPS 证书」。vim /etc/docker/daemon.json {insecure-registries: [192.168.80.185:5000]}systemctl restart docker运行registry服务docker run -itd -v /data/registry:/var/lib/registry -p 5000:5000 --restartalways --name registry registry:latest上传镜像# 给镜像打tagdocker tag alpine:latest 192.168.80.185:5000/alpine:latest # 上传到私有仓库docker push 192.168.80.185:5000/alpine:latest拉取镜像要配置insecure-registriesdocker pull 192.168.80.185:5000/alpine