Docker 学习笔记

一、快速开始

以快速启动一个 MySQL 的 Docker 容器为例:

docker run -d \
  --name mysql \
  -p 3306:3306 \
  -e TZ=Asia/Shanghai \
  -e MYSQL_ROOT_PASSWORD=123 \
  mysql

命令解读:

docker run -d # 创建并运行一个容器,-d则是让容器以后台进程运行

--name mysqlname  # 给容器起个名字叫 mysqlname

-p 3306:3306 # 端口映射,格式为: -p 宿主机端口:容器内端口

-e TZ=Asia/Shanghai # 配置容器运行时的参数
# 格式:-e KEY=VALUE,KEY和VALUE都由容器内进程决定
# 需要查看镜像文档来确定KEY和VALUE

mysql  # 设置镜像名称,Docker会根据这个名字搜索并下载镜像
二、Docker 引擎的安装

1️⃣ Linux下输入如下命令

curl -fsSL https://get.docker.com | bash -s docker --mirror Aliyun
# 若安装失败,请检查 yum 源是否正常
systemctl start docker     # 启动服务
systemctl enable docker    # 开机自启

2️⃣ 查看版本以验证是否正确运行

docker version

三、Docker Images

Docker Images 类似于虚拟机镜像,是存放在 docker 仓库(Registry)的文件,是用于创建 docker 容器的模板。

3.1 镜像加速

国内从 DockerHub 拉取镜像有时会遇到困难,此时可以配置镜像加速器。Docker 官方和国内很多云服务商都提供了国内加速器服务,例如:

云服务商地址
阿里云https://<你的ID>.mirror.aliyuncs.com

在 /etc/docker/daemon.json 中写入如下内容(如果文件不存在请新建该文件)

{"registry-mirrors": ["镜像站网址"] }  // 可以看到这里是一个list

设置完之后需要重启 docker 服务

sudo systemctl daemon-reload
sudo systemctl restart docker

3.2 获取镜像

docker pull REPOSITORY:TAG   # 获取指定版本的镜像

如果没加TAG,则默认拉取latest版本

3.3 镜像操作

1️⃣ 查看镜像信息

docker inspect 镜像名

2️⃣ 搜索镜像

docker search centos   # 以搜索centos镜像为例

3️⃣ 删除镜像

docker rmi <镜像名>

4️⃣ 导入或导出镜像

① save命令

docker save -o nginx.tar nginx:latest     # "-o"也可用">"替代

# -o 和 > 表示输出到文件,nginx.tar为目标文件名,nginx:latest是源镜像

② load命令 

docker load -i nginx.tar          # "-i"也可用"<"替代

# -i 和 < 表示从文件输入。会导入镜像及相关元数据,包括tag信息

四、容器操作

1️⃣ 运行容器,并且将主机的8080端口映射到容器的80端口上

docker run -d --name=nginx -p 8080:80 nginx
# -d 指定容器在后台运行
# --name 指定容器名

2️⃣ 进入容器交互

通过指定 -it 参数来保持标准输入打开,并且分配一个伪终端,是通过exec命令对容器执行操作时最为推荐的方式。使用docker exec --help 查看详细参数用法。

docker exec -it ContainerID /bin/bash    # 或者去掉"/bin/"

注:ContainerID 使用 docker ps -a 来查询

3️⃣ 容器常用操作

docker ps -a  # 先查看在运行的容器,记下CONTAINER ID
docker rm -f <容器id>  # 删除指定容器
docker stop <容器id>   # 停止指定容器
docker start <容器id>  # 启动指定容器
docker restart <容器id> # 重新启动容器
docker logs <容器id> # 重新启动容器
# 注:容器id可以只取前三位

🚮 容器退出后自动删除:

docker run -it --rm nginx bash
# --rm 参数和 -d 参数不能同时使用

五、数据挂载

5.1 数据卷(volume)挂载

数据卷是一个虚拟目录,是容器内目录宿主机目录之间映射的桥梁

docker run -d --name nginx -p 80:80 -v html:/usr/share/nginx/html nginx
# -v 创建html数据卷,并映射到/usr/share/nginx/html

docker volume ls  # 查看所有数据卷
# 结果:
DRIVER    VOLUME NAME
local     29524ff09715d3688eae3f99803a2796558dbd00ca584a25a4bbc193ca82459f
local     html

docker volume inspect html  # 查看数据卷详情
# 结果
[
    {
        "CreatedAt": "2024-05-17T19:57:08+08:00",
        "Driver": "local",
        "Labels": null,
        "Mountpoint": "/var/lib/docker/volumes/html/_data",
        "Name": "html",
        "Options": null,
        "Scope": "local"
    }
]

# 查看/var/lib/docker/volumes/html/_data目录
ll /var/lib/docker/volumes/html/_data   # 等价于 ls -l,表示以长格式(-l)列出

# 进入该目录,并随意修改index.html内容
cd /var/lib/docker/volumes/html/_data
vi index.html

出于容器内程序的存储需要,启动容器时未分配数据卷的容器,docker会自动为其创建一个“匿名数据卷”。演示一下查看MySQL的匿名数据卷:

# 查看MySQL容器详细信息
docker inspect mysql
# 关注其中.Config.Volumes部分和.Mounts部分
5.2 本地目录挂载

这是更加推荐的方式

# 挂载本地目录
-v 本地目录:容器内目录
# 挂载本地文件
-v 本地文件:容器内文件

容器内的目录是啥呢🧐?请参考 DockerHub:mysql - Official Image | Docker Hub

区别于5.1的volume挂载,本地目录或文件必须以 / 或 ./开头,如果直接以名字开头,会被识别为数据卷名而非本地目录名。

命令
说明
文档地址
docker volume create
创建数据卷
docker volume ls
查看所有数据卷
docker volume rm
删除指定数据卷
docker volume inspect
查看某个数据卷的详情
docker volume prune
清除数据卷

六、制作镜像 —— Dockerfile

Dockerfile 是一个没有后缀名的文本文件,其中包含了一个个指令,声明了构建目标镜像需要的步骤。常见的指令如下:

指令
说明
示例
FROM
指定基础镜像
FROM centos:6
ENV
设置环境变量,可在后面指令使用
ENV key value
COPY
拷贝本地文件到镜像的指定目录
COPY ./xx.jar /tmp/app.jar
RUN
执行Linux的shell命令,一般是安装过程的命令
RUN yum install gcc
EXPOSE
指定容器运行时监听的端口,是给镜像使用者看的
EXPOSE 8080
CMD
容器启动时的默认命令(可被覆盖)
CMD ["python", "app.py"]
ENTRYPOINT
镜像中应用的启动命令,容器运行时调用
ENTRYPOINT ["echo", "Hi"]

部署 Java 应用的简单示例:

# 使用官方 OpenJDK 17 基础镜像(轻量级)
FROM openjdk:17-jdk-alpine

# 设置工作目录
WORKDIR /app

# 将本地的 JAR 包复制到容器中
COPY target/my-spring-boot-app.jar app.jar

# 暴露应用监听的端口(如 Spring Boot 默认的 8080)
EXPOSE 8080

# 定义容器启动时执行的命令
ENTRYPOINT ["java", "-jar", "/app/app.jar"]

另一个 Dockerfile 示例:

# 指定基础镜像
FROM centos:7

# 维护者信息
MAINTAINER Cao Huan <huan_@outlook.com>

# 使用yum安装php
RUN yum install php-cli php-process php-devel php-pear libevent-devel -y

# 将当前目录下的BrowserQuest.tar.gz文件复制到镜像/root/web/文件夹下
COPY BrowserQuest.tar.gz /root/web/

# 修改镜像工作路径为/root/web/
WORKDIR /root/web/

# 解压当前工作路径下的 BrowserQuest.tar.gz 文件
RUN tar xzvf BrowserQuest.tar.gz

# 定义构建参数(可在构建时通过 --build-arg 指定)
ARG HOST_IP=localhost

# 生成 setup.sh 脚本,使用双引号允许变量替换,并转义内部的双引号
RUN echo -e "sed -i 's/hostip/$HOST_IP/g' Web/config/config_local.json && \
    php start.php start" > BrowserQuest/setup.sh

# 添加可执行权限
RUN chmod +x BrowserQuest/setup.sh

# 修改镜像工作路径为 /root/web/BrowserQuest
WORKDIR /root/web/BrowserQuest

# 创建Docker容器创建时执行的命令,即修改hostip并启动游戏服
CMD ["bash","setup.sh"]

# 声明镜像内服务监听的端口
EXPOSE 8000 8787

使用案例:上传 Gitee 代码,Jenkins 自动执行脚本,使用docker构建并部署应用

Jenkins 完成构建后执行的脚本:

#!/bin/bash

echo "开始制作镜像文件"

# 切换到项目根目录
cd /home/cat-community/

# 构建 Docker 镜像
docker build -t cat-community:latest .

# 暂停原服务
docker stop cat-community || true

# 删除原服务容器
docker rm cat-community || true

# 启动新服务(通过环境变量传递 JVM 参数)
# java ... 是容器启动时执行的具体命令
# > console.log: 将容器启动日志输出到 console.log 文件(覆盖模式)
docker run \
  --name cat-community \
  -p 8080:8080 \
  -e JAVA_OPTS="-Xms512m -Xmx768m" \
  -d \
  cat-community

# 捕获容器日志
docker logs -f cat-community > console.log &

echo "部署完成"

Jenkins 自动部署使用的 Dockerfile:

# 使用官方 OpenJDK 17 基础镜像
FROM openjdk:17

# 设置时区为上海
RUN /bin/cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtime && \
    echo 'Asia/Shanghai' > /etc/timezone

# 复制 JAR 包
COPY cat-community-0.0.1.jar /home/cat-community/cat-community-0.0.1.jar

# 定义 JVM 参数环境变量(默认为空)
ENV JAVA_OPTS=""

# 暴露端口
EXPOSE 8080

# 启动命令:使用 sh -c 支持环境变量替换,是 shell 的语法而非 docker 的
CMD ["sh", "-c", "java $JAVA_OPTS -jar /home/cat-community/cat-community-0.0.1.jar --server.port=8080"]
# ============ 仅使用 CMD 示例 ============
# Dockerfile: CMD 示例
CMD ["echo", "Hello from CMD"]
# 构建镜像:
# docker build -t cmd-example .
# 运行容器 (默认):
# docker run cmd-example
# 输出:
# Hello from CMD
# 覆盖 CMD:
# docker run cmd-example echo "Overridden CMD"
# 输出:
# Overridden CMD


# ============ 仅使用 ENTRYPOINT 示例 ============
# Dockerfile: ENTRYPOINT 示例
ENTRYPOINT ["echo", "Hello from ENTRYPOINT"]
# 构建镜像:
# docker build -t entrypoint-example .
# 运行容器 (默认):
# docker run entrypoint-example
# 输出:
# Hello from ENTRYPOINT
# 覆盖 ENTRYPOINT:
# docker run --entrypoint "/bin/sh" entrypoint-example -c "echo Overridden ENTRYPOINT"
# 输出:
# Overridden ENTRYPOINT


# ============ ENTRYPOINT + CMD 示例 ============
# Dockerfile: ENTRYPOINT + CMD 示例
ENTRYPOINT ["echo", "Hello"]
CMD ["from CMD"]
# 构建镜像:
# docker build -t combined-example .
# 运行容器 (默认):
# docker run combined-example
# 输出:
# Hello from CMD
# 覆盖 CMD 参数:
# docker run combined-example "Custom CMD"
# 输出:
# Hello Custom CMD

七、容器间网络

要想实现容器之间的网络互联,可以通过docker为每个容器分配的默认网段(使用docker的默认网卡),也可以用docker新建网络(网桥),用于特定的一批需要互联的容器

7.1 Docker 默认网络通信

# 查看某个容器的默认IP:
# 用基本命令,寻找Networks.bridge.IPAddress属性
docker inspect mysql
# 得到IP如下:172.17.0.2,该IP属于docker0网卡(ip addr 命令查看)

此处得到的IP可以用于容器之间直接通信。进入另一个容器执行

docker exec -it <另一个容器id> bash  # 进入另一个容器
ping 172.17.0.2  # 是可以 ping 通的

7.2 自定义网络

# 1、创建网络
docker network create <网络名>

# 2、查看网络
docker network ls

# 3、让某些容器,都加入该网络,加入网络时可以通过 --alias 给容器起别名
# 别名可以用于该网络内的其它容器互相访问
docker network connect <网络名> <容器名> --alias <别名>

# 4、同样可以进入容器内 ping 测试【同一网络内容器】的连通性
docker exec -it <容器id> bash
ping <容器名> 或 <别名>

八、Docker Compose

8.1 语法对比案例

用docker run部署MySQL的命令如下:

docker run -d \
  --name mysql \
  -p 3306:3306 \
  -e TZ=Asia/Shanghai \
  -e MYSQL_ROOT_PASSWORD=123 \
  -v ./mysql/data:/var/lib/mysql \
  -v ./mysql/conf:/etc/mysql/conf.d \
  -v ./mysql/init:/docker-entrypoint-initdb.d \
  --network hmall
  mysql

如果用docker-compose.yml文件来定义,就是这样:

version: "3.8"

services:
  mysql:
    image: mysql
    container_name: mysql
    ports:
      - "3306:3306"
    environment:
      TZ: Asia/Shanghai
      MYSQL_ROOT_PASSWORD: 123
    volumes:
      - "./mysql/conf:/etc/mysql/conf.d"
      - "./mysql/data:/var/lib/mysql"
    networks:
      - new
networks:
  new:
    name: hmall

8.2 完整案例

version: "3.8"

services:
  mysql:
    image: mysql
    container_name: mysql
    ports:
      - "3306:3306"
    environment:
      TZ: Asia/Shanghai
      MYSQL_ROOT_PASSWORD: 123
    volumes:
      - "./mysql/conf:/etc/mysql/conf.d"
      - "./mysql/data:/var/lib/mysql"
      - "./mysql/init:/docker-entrypoint-initdb.d"
    networks:
      - hm-net
  hmall:
    build: 
      context: .
      dockerfile: Dockerfile
    container_name: hmall
    ports:
      - "8080:8080"
    networks:
      - hm-net
    depends_on:
      - mysql
  nginx:
    image: nginx
    container_name: nginx
    ports:
      - "18080:18080"
      - "18081:18081"
    volumes:
      - "./nginx/nginx.conf:/etc/nginx/nginx.conf"
      - "./nginx/html:/usr/share/nginx/html"
    depends_on:
      - hmall
    networks:
      - hm-net
networks:
  hm-net:
    name: hmall

8.3 常见命令

# 启动所有, -d 参数是后台启动​
docker compose up -d
类型
参数或指令
说明
Options
-f
指定compose文件的路径和名称
-p
指定project名称。project就是当前compose文件中设置的多个service的集合,是逻辑概念
Commands
 
up
创建并启动所有service容器
down
停止并移除所有容器、网络
ps
列出所有启动的容器
logs
查看指定容器的日志
stop
停止容器
start
启动容器
restart
重启容器
top
查看运行的进程
exec
在指定的运行中容器中执行命令

九、常见命令

 

命令
说明
文档地址
docker pull
拉取镜像
docker push
推送镜像到DockerRegistry
docker images
查看本地镜像
docker rmi
删除本地镜像
docker run
创建并运行容器(不能重复创建)
docker stop
停止指定容器
docker start
启动指定容器
docker restart
重新启动容器
docker rm
删除指定容器
docker ps
查看容器
docker logs
查看容器运行日志
docker exec
进入容器
docker save
保存镜像到本地压缩文件
docker load
加载本地压缩文件到镜像
docker inspect
查看容器详细信息
This article was updated on