docker零基础入门|2025重置版
之前我录个docker的零基础教程,是23年5月份的时候 那个时候docker换个国内源就可以正常拉取镜像了 但是24年之后,受到众所周知的原因,所有的国内docker源都不行了 所以大大增
docker零基础入门|2025重置版
发布时间:2025-11-16 (2025-11-16)

之前我录个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 [选项] <镜像名:标签> [容器启动参数]

选项

  1. --name 表示为启动的容器起个名字,这个名字在宿主机上唯一
  2. -t 为docker分配一个伪终端并绑定到容器的标准输入上
  3. -i 是让容器的标准输入保持打开状态
  4. -v 目录映射,实现数据的持久化,冒号前面表示宿主机的目录,后面是容器内目录。目录不存在会自动生成。
  5. -p 端口映射,宿主机端口:容器端口
  6. -e 表示要设置环境变量
  7. -d 表示要以分离模式(也就是后台模式)启动容器,这样执行后会返回容器ID,不会进入交互界面。如果想要进入交互界面需要-i 和-t参数。
  8. --restart 表示容器重启策略
  9. --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(命名空间)中启动一个新的终端进程(如 shbash),从而可以查看容器内文件、执行命令、调试 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

容器常见问题

容器启动就报错?

  1. 先docker logs看日志,如果日志有错误输出,基本上就知道怎么去解决
  2. 没有日志?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 服务 + 数据库 + 缓存 + 消息队列),手动操作会面临以下痛点:

  1. 需逐个运行 docker run 命令,参数冗长(端口、网络、数据卷、环境变量等);
  2. 需手动维护容器间的网络连通(如让 Web 容器访问 DB 容器);
  3. 容器启动顺序有依赖(必须先启动 DB,再启动 Web);
  4. 服务扩容、重启、日志查看需逐个操作。

而 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