之前我录个docker的零基础教程,是23年5月份的时候

那个时候docker换个国内源就可以正常拉取镜像了
但是24年之后,受到众所周知的原因,所有的国内docker源都不行了
所以大大增加了入门难度
再加上内容有点浅显了,本课程再加点内容,并且前期尽量讲的通俗易懂
docker能干啥
试想一个场景,你开在windows上开发的一个web项目
好巧不巧,你是用python开发的,然后部署到服务器的时候,先是费劲的安装mysql,redis,nginx这些中间件,然后跑python项目,发现死活跑不起来,要不就提示少包,要不就提示函数不存在,咋回事呢?
使用docker之后,以后安装mysql、redis、es、nginx这些中间件这些就只需要一条命令即可
并且以后做的项目,项目部署也只需要一两条命令即可,大大方便了开发安装、部署流程
能看到这个文档的,我相信大家应该很焦虑啊,网上说啥的都有,什么docker不行了,现在都是k8s了,podman未来要取代docker等等
我反正就一句话,该学学,该用用,真的淘汰了就去拥抱新变化
我之前也学了jQuery,还用django写了前后端不分离的网站
但是也不妨碍我现在又学了go,用前后端分离的技术写网站
什么是docker
docker是一个用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
# 启动docker
systemctl 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 makecache
docker设置代理
从2024年开始,中国境内就不能在通过docker换国内源下载镜像了
这一点确实劝退了很多人
如果不会魔法的同学,可以使用腾讯云的服务器,腾讯云有docker私有源,可以下载镜像
也可以买一个境外、国外的服务器,也可以直接拉取docker镜像
真要随心所欲的拉取镜像,最好的方法还是学会魔法,以及配置docker代理
当然,本课程只能教你如何配置docker代理
创建 /etc/systemd/system/docker.service.d/http-proxy.conf 配置文件
[Service]
Environment="HTTP_PROXY=http://192.168.80.1:7890"
Environment="HTTPS_PROXY=http://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 --name=mysql -e MYSQL_ROOT_PASSWORD=123456 mysql:5.7
然后用你的mysql客户端,就可以连上你的mysql了,爽死
好了,我相信docker应该勾起了你的学习兴趣了,那么课程正式开始!
如果访问不了,大概率是端口没开
firewall-cmd --list-ports
firewall-cmd --zone=public --add-port=80/tcp --permanent
firewall-cmd --reload
docker镜像
学习docker,从镜像开始
上面我提到的mysql、redis等,在dockerhub上就会有大佬或者官方把这些中间件制作成docker镜像
我们只需要使用docker pull 镜像名 就可以下载到我们本地了
镜像的基本操作
# 拉取镜像
docker pull image_name:tag
# 不写tag,默认是latest
# 检索镜像
docker search image_name:tag
# 列表
docker images
# 删除镜像
docker rmi image_id
docker容器
可以理解为一个非常精简的操作系统
在这个操作系统上,只部署了你需要的服务,也就是你对应的镜像
比如nginx的镜像,它运行的容器,就表示这个操作系统里面只有nginx这个软件服务
比如mysql的镜像,它运行的容器,就表示这个操作系统里面只有mysql这个软件服务
容器是 Docker 最核心的概念,本质是基于镜像运行的隔离进程—— 它复用宿主机内核,通过 Namespace、Cgroups 等技术实现资源隔离(网络、文件系统、进程)和限制(CPU、内存),比虚拟机更轻量、启动更快。
容器列表
# 查看正在运行中的容器
docker ps
# 查看所有容器
docker ps -a
# 只获取容器id
docker 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表示是否使用特权模式,设置–privileged=true提升系统执行权限。设置为true后,容器内的root用户才是真正的root权限,否则只是一个普通用户。
容器运行
以mysql为例
# 后台运行 并且 暴露端口 -p 宿主机端口:容器内端口
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 -p 3307:3306 mysql:5.7
# 传递环境变量 配置数据库名
docker run -d --name mysql -p 3306:3306 -e MYSQL_ROOT_PASSWORD=123456 -e MYSQL_DATABASE=blog mysql:5.7
# 数据挂载 重启mysql,数据也不会丢失 -v 宿主机路径:容器内路径
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 -v ./mysql_data:/var/lib/mysql mysql:5.7
# 限制 1G 内存、1 个 CPU 核心,超出内存时容器自动终止
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 -p 3306:3306 --memory 1G --cpus 1 mysql:5.7
# 容器重启策略:always(总是重启,除非手动停止)
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 --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条日志 老版本docker
docker logs -f --tail=10 <容器名称>
进入容器
容器本质是隔离的进程,进入容器 本质是在该隔离进程的 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_PASSWORD=123456 -p 3307:3306 mysql:5.7
# 宿主机随机端口 → 容器 3306 端口
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 -p 3306 mysql:5.7
# 仅 192.168.80.185 这个网卡的 3306 端口映射到容器 3306
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 -p 192.168.80.185.100:3306:3306 mysql:5.7
# 映射 多个端口
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 -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) 和 数据卷(Volume),Go 场景中前者用于配置文件 / 日志,后者用于重要数据(如数据库)。
绑定挂载
直接将宿主机的自定义目录 / 文件挂载到容器,适合需要手动修改、查看的场景(如配置文件、日志目录)。
docker run -v <宿主机路径>:<容器内路径>[:权限] <镜像名>
# ro:只读(容器内只能读取,不能修改宿主机文件);
# rw:读写(默认,容器内可修改宿主机文件)。
# 宿主机 ./mysql_data 目录 → 容器 /var/lib/mysql 目录(读写权限)
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 -v ./mysql_data:/var/lib/mysql mysql:5.7
# 目录映射之后,删除容器再执行运行命令,数据依然还在
# 宿主机 ./xxx.txt 文件 → 容器 /etc/mysql/xxx.txt 文件(只读权限,防止容器修改)
docker run -d --name mysql -e MYSQL_ROOT_PASSWORD=123456 -v ./xxx.txt:/xxx.txt:ro mysql:5.7
要注意,直接用vi、vim修改文件,很可能是不会同步的,因为vi编辑是创建一个新文件然后把老文件删掉,再把新文件替换的方式,会打破docker的同步机制
数据卷(Volume)
Docker 管理的持久化存储
数据卷是 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_PASSWORD=123456 -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示例
# 注释:指定基础镜像(必填,所有指令的起点)
FROM golang:alpine AS builder
# 设置环境变量
ENV CGO_ENABLED 0
ENV GOPROXY https://goproxy.cn,direct
RUN 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 main
FROM alpine
WORKDIR /app
# 维护者信息
LABEL maintainer="fengfeng@xx.com"
LABEL version="1.0"
LABEL description="A demo Docker image for go app"
# 元数据可通过 docker inspect <镜像名> 便于镜像管理
# 多阶段构建
COPY --from=builder /build/main /app
RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.aliyun.com/g' /etc/apk/repositories
RUN apk add bash
# 容器启动时执行的命令(必填,仅最后一个生效)
CMD ["./main"]
# 暴露端口(仅声明,不实际映射,用于文档说明)
EXPOSE 8080
构建命令
# 完整命令
docker build -f Dockerfile -t demo-app:v1.0 .
# 可省略-f
docker 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、zip)
ADD . /app/
RUN、CMD和ENTRYPOINT
run:在镜像构建过程中执行命令(如安装依赖、解压文件、创建目录),执行结果会被固化到镜像的分层中。
两种格式
# 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:默认启动参数
# 这里默认指定 --env=dev 和 --port=8080,运行时可替换
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 --net=host go-demo:1.0 --port 8080
docker compose
Docker Compose 是 Docker 官方提供的多容器应用编排工具,核心作用是通过一个统一的配置文件(默认 docker-compose.yml)定义、管理多个关联的 Docker 容器,实现「一键启动 / 停止 / 销毁」整个应用集群,解决了手动逐个操作容器(如网络配置、依赖顺序、端口映射)的繁琐问题。
当你的应用依赖多个服务时(比如:Web 服务 + 数据库 + 缓存 + 消息队列),手动操作会面临以下痛点:
- 需逐个运行
docker run命令,参数冗长(端口、网络、数据卷、环境变量等); - 需手动维护容器间的网络连通(如让 Web 容器访问 DB 容器);
- 容器启动顺序有依赖(必须先启动 DB,再启动 Web);
- 服务扩容、重启、日志查看需逐个操作。
而 Docker Compose 可以:
- 用一个 YAML 文件声明所有服务配置;
- 一键启动 / 停止所有服务(
docker-compose up/down); - 自动创建独立网络,让容器间通过服务名互通;
- 维护容器依赖关系、数据卷持久化、环境变量隔离;
- 统一查看所有服务日志、扩容服务实例。
基本结构
# 版本声明(推荐 v3,兼容 Docker 17.06+)
version: '3'
# 定义所有服务(核心模块,每个服务对应一个容器)
services:
服务1名称:
# 镜像(必填):来自 Docker Hub 或自定义镜像
image: 镜像名:标签
# 容器名称(可选,默认自动生成)
container_name: 容器名
# 端口映射(可选):主机端口:容器端口
ports:
- "主机端口1:容器端口1"
- "主机端口2:容器端口2"
# 环境变量(可选):两种写法
environment:
- 键=值 # 直接写
- 键2=值2
env_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.06+)
version: '3'
# 定义所有服务(核心模块,每个服务对应一个容器)
services:
web:
# 镜像(必填):来自 Docker Hub 或自定义镜像
image: web:v1
# 容器名称(可选,默认自动生成)
# container_name: 容器名
# 端口映射(可选):主机端口:容器端口
ports:
- "8081:8080"
# 环境变量(可选):两种写法
environment:
- xxx=dev # 直接写
# 网络(可选):指定服务加入的网络
networks:
- xx_net
# 重启策略(可选):容器退出后的重启规则
restart: always # 可选:no/on-failure/unless-stopped
web1:
image: web:v1
ports:
- "8082:8080"
networks:
- xx_net
restart: always
# 定义网络(可选,默认会创建一个默认网络)
networks:
xx_net:
driver: bridge # 网络驱动(默认 bridge,支持 overlay 等)
常用命令
# 后台启动所有服务
docker compose up -d
# 查看 web 服务的实时日志
docker compose logs -f web
# 进入 db 服务容器(如 MySQL)
docker compose exec db bash
# 停止所有服务(保留容器和数据卷)
docker compose stop
# 停止并删除所有服务、网络、容器(保留数据卷)
docker compose down
# 停止并删除所有(包括数据卷)
docker compose down -v
实战:go+mysql部署
services:
blog:
image: web:v3
networks:
- blog
restart: always
volumes:
- ./settings.yaml:/app/settings.yaml
depends_on:
- mysql
mysql:
image: mysql:5.7
environment:
- MYSQL_ROOT_PASSWORD=123456
- MYSQL_DATABASE=blog
volumes:
- ./mysql_data:/var/lib/mysql
networks:
- blog
restart: always
# 定义网络(可选,默认会创建一个默认网络)
networks:
blog:
driver: bridge # 网络驱动(默认 bridge,支持 overlay 等)
go语言对接docker api
有些情况下,需要通过代码实现对容器的操作
比如获取容器列表,获取镜像列表,创建容器,删除容器
package main
import (
"context"
"fmt"
"github.com/docker/docker/api/types/container"
"github.com/docker/docker/api/types/image"
"github.com/docker/go-connections/nat"
"log"
"github.com/docker/docker/client"
)
var cli *client.Client
func init() {
// 初始化 Docker 客户端(使用默认端点)
// 如需自定义端点(如远程 Docker 服务),可通过 client.WithHost("tcp://192.168.1.100:2375") 指定
_cli, err := client.NewClientWithOpts(client.FromEnv, client.WithAPIVersionNegotiation())
if err != nil {
log.Fatalf("创建 Docker 客户端失败:%v", err)
}
//defer cli.Close() // 退出时关闭客户端连接
fmt.Println("Docker 客户端连接成功!")
cli = _cli
}
func imageList() {
list, _ := cli.ImageList(context.Background(), image.ListOptions{})
for _, summary := range list {
fmt.Printf("%s %s\n", summary.ID[7:19], summary.RepoTags)
}
}
func containerList() {
list, _ := cli.ContainerList(context.Background(), container.ListOptions{})
for _, summary := range list {
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)
func containerCreate() {
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",
)
if err != nil {
fmt.Println("创建容器失败", err)
return
}
fmt.Println("容器创建成功", response.ID)
err = cli.ContainerStart(context.Background(), response.ID, container.StartOptions{})
if err != nil {
fmt.Println("容器启动失败", err)
return
}
fmt.Println("容器启动成功")
}
func main() {
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 --restart=always --name registry registry:latest
上传镜像
# 给镜像打tag
docker tag alpine:latest 192.168.80.185:5000/alpine:latest
# 上传到私有仓库
docker push 192.168.80.185:5000/alpine:latest
拉取镜像
要配置insecure-registries
docker pull 192.168.80.185:5000/alpine