ARTICLE DETAIL

建站实战干货

来自一线的建站与推广经验沉淀,每一条都经过真实交付验证。

一文读懂:Docker从入门到进阶(超详细实践应用、零踩坑)

2026/9/21 10:58:31 拓冰建站 浏览量
一文读懂:Docker从入门到进阶(超详细实践应用、零踩坑)

文章目录

  • 快速入门
    • 简介
    • 安装
    • 配置镜像加速
    • 部署MySQL
  • Docker基础
    • 常见命令
    • 案例-部署Nginx
    • 命令别名
    • 数据卷挂载
    • 本地目录挂载
    • DockerFile语法
    • 自定义镜像
    • 容器网络互联
  • 项目部署
    • 部署Java应用
    • 部署前端
    • DockerCompose

快速入门

简介

Docker是一个快速构建、运行、管理应用的工具。

传统的企业项目部署需要手动通过命令行安装环境和依赖,安装步骤复杂且常常会出现版本不兼容等问题。

借助Docker,我们只需要执行docker run命令即可一键让项目及其依赖在服务器成功运行起来。

由于项目通常在Linux系统部署,正式学习Docker前可以通过Vmware虚拟机安装CentOS系统。

Docker官网:https://www.docker.com/

安装

  1. 配置Docker的yum仓库:

    yum install -y yum-utils
    yum-config-manager --add-repo https://download.docker.com/linux/centos/docker-ce.repo
    
  2. 安装Docker:

    yum install -y docker-ce docker-ce-cli containerd.io docker-buildx-plugin docker-compose-plugin
    
  3. 启动和校验:

    # 启动Docker
    systemctl start docker# 停止Docker
    systemctl stop docker# 重启
    systemctl restart docker# 设置开机自启
    systemctl enable docker# 执行docker ps命令,如果不报错,说明安装启动成功
    docker ps
    

配置镜像加速

推荐使用阿里云的镜像加速服务。

  1. 找到产品->容器->容器镜像服务ACR。

    image-20240210182916494
  2. 找到镜像加速器并按照文档配置即可:

    image-20240210185642768

部署MySQL

用Docker部署MYSQL只需要执行如下命令即可:

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

当我们执行这个命令时,Docker会自动搜索并下载镜像(Image)。镜像不仅包含应用本身,还包含应用所需的环境、配置、函数库。

Docker会在运行镜像时创建一个隔离环境,称为容器(Container)。Docker官方维护了一个公共镜像Docker Hub用于存储和管理镜像。

下面详细介绍这条命令:

  • docker run:创建并运行一个容器,-d是让容器在后台运行。
  • –name mysql:给容器命名,必须唯一。
  • -p 3306:3306:端口映射,容器有自己的隔离网络系统,通过-p [宿主机端口]:[容器端口]可以进行端口映射。
  • -e:设置环境变量,-e KEY=VALUE。根据镜像的文档说明设置。
  • mysql:镜像名称,完整写法:[repository]:[tag],即[镜像名]:[版本]。默认是latest最新版本。

Docker基础

常见命令

image-20240211162755685

我们可以从镜像仓库拉取镜像或向镜像仓库推送镜像,常用命令:

  • docker push:将镜像推送到镜像仓库

  • docker pull:从镜像仓库拉取镜像到本地仓库

  • docker images:查看本地镜像

  • docker rmi:删除本地镜像

除了在镜像仓库获取镜像,也可以在本地编写dockerfile构建自己的镜像:

  • docker build:根据编写的dockerfile构建镜像

为了方便镜像传输,可以将镜像打包成压缩文件,常用命令:

  • docker save:将镜像打包成压缩文件保存在本地
  • docker load:将压缩文件加载到docker本地镜像仓库

有了镜像,可以通过如下命令创建容器并启动镜像:

  • docker run:创建并运行容器(如本地没有镜像会自动拉取)

对容器进行管理的常用命令:

  • docker stop:停止容器进程
  • docker start:启动容器进程
  • docker ps:查看运行中的容器
  • docker rm:删除容器
  • docker logs:查看容器运行日志
  • docker exec:进入容器内部执行命令

案例-部署Nginx

掌握了Docker的常见命令,现在来完成一个简单的案例,创建并运行Nginx容器。

  1. 首先,在Docker Hub搜索Nginx镜像,查看镜像名称。

    image-20240211163131603
  2. 通过docker pull nginx拉取镜像到本地,然后使用docker images查看本地镜像:

    image-20240211163301289
  3. 通过docker run -d --name nginx -p 80:80 nginx命令创建并运行容器,然后使用docker ps查看运行中的容器:

    image-20240211163632620
  4. 通过docker stop nginx停止容器,然后通过docker ps -a查看所有容器:

    image-20240211163920272
  5. 通过docker start nginx再次启动容器:

    image-20240211164005758
  6. 通过docker logs -f nginx查看日志,-f指follow持续输出日志(无法访问请关闭centos防火墙):

    image-20240211164443572
  7. 通过docker exec -it nginx bash命令进入容器并使用bash进行交互,-it指可交互终端。

    image-20240211164656153

命令别名

前面我们通过docker ps可以查看容器运行状态,但是输出的信息比较多。ps命令可以通过–format参数自定义输出格式:

docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"
image-20240211165126529

但是这个命令太复杂,我们可以借助Linux的别名来简化这个操作。通过vim ~/.bashrc命令编辑加入下面的指令:

alias dps='docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"'
image-20240211173337115

然后执行source ~/.bashrc,测试dps命令:

image-20240211173418654

数据卷挂载

我们以一个需求为例:

  1. 创建nginx容器,修改nginx容器内的html目录下的index.html文件。
  2. 将静态资源部署到nginx的html目录。

在Docker Hub查看nginx镜像官方文档,发现/usr/share/nginx/html为静态资源目录:

image-20240211173808064

通过docker exec -it nginx bash进入容器内部控制台,查看静态资源目录:

image-20240211173943054

通过vi index.html修改静态资源,发现不存在vi指令。原因是docker镜像包含的是服务运行所必须的依赖,而vi是非必须的。

image-20240211174324089

为了解决这个问题,我们可以使用数据卷:数据卷(volume)是一个虚拟目录,是容器内目录宿主机目录之间映射的桥梁。

我们创建数据卷,会在宿主机系统的/var/lib/docker/volumes/目录下创建相应的文件夹。容器和宿主机文件系统就可以实现双向绑定。

image-20240211174533872

如何创建数据卷呢?Docker为我们提供了相应的命令:

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

在执行docker run命令时,使用 -v 数据卷:容器内目录 可以完成数据卷挂载。如果挂载的数据卷不存在会自动创建数据卷。

执行docker run -d --name nginx -p 80:80 -v html:/usr/share/nginx/html nginx命令,挂载数据卷且创建并运行容器。

然后通过docker volume ls和docker volume inspect查看数据卷,容器内的html文件夹会映射到/var/lib/docker/volumes/html/_data文件夹。

image-20240211180901012

我们只需要修改/var/lib/docker/volumes/html/_data下的index.html即可:

image-20240211181106381

本地目录挂载

通过docker inspect可以查看容器详情,其中的Mounts代表挂载的数据卷,查看mysql容器:

image-20240211181530861

我们创建mysql容器时虽然没有创建和指定数据卷,但它还是挂载到了一个数据卷。这个数据卷由容器自行创建,称为匿名卷。

匿名卷映射了容器的/var/lib/mysql目录,这个目录存储了mysql的数据。匿名卷目录名非常长,不方便数据访问,最好在启动mysql容器时指定数据卷。

除了之前的数据卷挂载,我们也可以挂载到宿主机的本地目录:docker run -v 本地目录:容器内目录 可以完成本地目录挂载。

对于mysql,除了数据目录,还需要挂载配置文件、初始化脚本目录,这些目录可以查询Docker Hub的官方文档:

image-20240211182323741 image-20240211182350714

我们可以执行如下挂载:

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

DockerFile语法

DockerFile是一个文本文件,包含一个个指令说明执行什么操作来构建镜像。常见指令:

指令说明示例
FROM指定基础镜像FROM centos:6
ENV设置环境变量,可在后面指令使用ENV key:value
COPY拷贝本地文件到镜像的指定目录COPY ./jre11.tar.gz /tmp
RUN执行Linux的shell命令,一般是安装过程的命令RUN tar -zxvf /tmp/jre11.tar.gz && EXPORTS path=/tmp/jre11:$path
EXPOSE指定容器运行时监听的端口,是给镜像使用者看的EXPOSE 8080
ENTRYPOINT镜像中应用的启动命令,容器运行时调用ENTRYPOINT java -jar xx.jar

例:

# 指定基础镜像
FROM ubuntu:16.04
# 配置环境变量,JDK的安装目录、容器内时区
ENV JAVA_DIR=/usr/local
# 拷贝jdk和java项目的包
COPY ./jdk8.tar.gz $JAVA_DIR/
COPY ./docker-demo.jar /tmp/app.jar
# 安装JDK
RUN cd $JAVA_DIR \&& tar -xf ./jdk8.tar.gz \&& mv ./jdk1.8.0_144 ./java8
# 配置环境变量
ENV JAVA_HOME=$JAVA_DIR/java8
ENV PATH=$PATH:$JAVA_HOME/bin
# 入口,java项目的启动命令
ENTRYPOINT ["java", "-jar", "/app.jar"]

也可以直接基于JDK为基础镜像,省略一些步骤:

# 基础镜像
FROM openjdk:11.0-jre-buster
# 拷贝jar包
COPY docker-demo.jar /app.jar
# 入口
ENTRYPOINT ["java", "-jar", "/app.jar"]

自定义镜像

编写完DockerFile后可以通过 docker build -t [repository]:[tag] . 命令构建镜像。

  • -t:给镜像起名,不指定tag时默认为latest。
  • .:指定DockerFile所在目录,如果在当前目录就使用"."。

容器网络互联

每个容器都有自己的隔离网络。默认情况下,所有容器都是以bridge方式连接到Docker的一个虚拟网桥上。

Docker的默认网桥是docker0:172.17.0.1/16,如图所示:

image-20240211185512385

默认情况下,不同容器是可以相互连通的。但是容器的ip地址是动态的,每次重启可能会发生变化。

我们可以自定义网络,加入自定义网络的容器可以通过容器名互相访问,Docker的网络操作命令如下:

命令说明
docker network create创建一个网络
docker network ls查看所有网络
docker network rm删除指定网络
docker network prune清除未使用的网络
docker network connet使指定容器加入某网络
docker network disconnect使指定容器离开某网络
docker network inspect查看网络详细信息
  1. 通过docker network create创建网络并通过docker network ls查看网络:

    image-20240211190048727
  2. 通过ip addr指令查看发现多了一个虚拟网桥:

    image-20240211190142941
  3. 通过docker network connect simplicity mysql将mysql容器加入simplicity网络,然后通过docker inspect mysql查看网络信息:

    image-20240211190328439

此时,同网络中的其它容器可以通过 ping mysql 来访问这个容器。

除了这种方式,还可以在创建容器时加入 –network simplicity 使容器加入指定网络。

项目部署

部署Java应用

  1. 配置数据库信息。实际项目开发中的数据库连接参数通常用$来引用其它配置文件的变量,根据运行环境不同选择不同的数据库配置信息:

    image-20240211191659540

    例如,在本地(local)使用测试数据库,而在服务器(dev)正式部署使用mysql容器名:

    image-20240211191801878
  2. 通过Maven的package命令打包:

    image-20240211192238262
  3. 编写DockerFile:

    # 基础镜像
    FROM openjdk:11.0-jre-buster
    # 设定时区
    ENV TZ=Asia/Shanghai
    RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone
    # 拷贝jar包
    COPY service.jar /app.jar
    # 入口
    ENTRYPOINT ["java", "-jar", "/app.jar"]
    
  4. 将jar包和DockerFile放到同一个目录下,然后使用docker build -t “mall” .构建镜像:

    image-20240211192840599
  5. 通过docker run -d --name mall -p 8080:8080 --network simplicity mall创建并运行容器并通过docker logs -f mall查看日志:

    image-20240211193029061
  6. 此时,后端项目在Docker部署成功。我们可以将DockerFile和jar文件打包,其它人就可以构建镜像并运行容器。

部署前端

部署完毕后端,我们再来部署前端。通常使用nginx作为前端服务,我们需要部署静态资源目录html和conf配置文件。

对于nginx的conf,我们常在其中配置静态资源的目录、端口和代理。如:

worker_processes  1;events {worker_connections  1024;
}http {include       mime.types;default_type  application/json;sendfile        on;keepalive_timeout  65;server {listen       18080;# 指定前端项目所在的位置location / {root /usr/share/nginx/html/mall-portal;}error_page   500 502 503 504  /50x.html;location = /50x.html {root   html;}location /api {rewrite /api/(.*)  /$1 break;proxy_pass http://mall:8080;}}server {listen       18081;# 指定前端项目所在的位置location / {root /usr/share/nginx/html/mall-admin;}error_page   500 502 503 504  /50x.html;location = /50x.html {root   html;}location /api {rewrite /api/(.*)  /$1 break;proxy_pass http://mall:8080;}}
}

上面的配置说明了用户端和管理端项目的端口、目录。此外,还通过代理转发了api接口到后端地址,解决了跨域的问题。

通过查询Docker Hub的官方文档,我们知道了nginx配置文件挂载方式:

image-20240211193753397

下面,开始通过Docker部署前端项目:

docker run -d \--name nginx \-p 18080:18080 \-p 18081:18081 \-v /root/nginx/html:/usr/share/nginx/html \-v /root/nginx/nginx.conf:/etc/nginx/nginx.conf \--network simplicity \nginx
image-20240211194218249

DockerCompose

尽管通过DockerFile直接Build镜像并创建容器运行已经很方便,但是对于Java项目我们需要分别运行Mysql、后端、前端的Docker容器,还是很繁琐。

DockerCompose为我们解决了这个问题,它通过一个单独的docker-compose.yml模板文件定义一组关联的容器,帮助我们实现多个关联容器的快速部署。

DockerCompose的简易模板:

image-20240211200253526

对于前面的项目部署,我们可以编写docker-compose.yml如下所示:

version: "3.8"services:mysql:image: mysqlcontainer_name: mysqlports:- "3306:3306"environment:TZ: Asia/ShanghaiMYSQL_ROOT_PASSWORD: 123volumes:- "./mysql/conf:/etc/mysql/conf.d"- "./mysql/data:/var/lib/mysql"- "./mysql/init:/docker-entrypoint-initdb.d"networks:- mall-netmall:build: context: .dockerfile: Dockerfilecontainer_name: mallports:- "8080:8080"networks:- mall-netdepends_on:- mysqlnginx:image: nginxcontainer_name: nginxports:- "18080:18080"- "18081:18081"volumes:- "./nginx/nginx.conf:/etc/nginx/nginx.conf"- "./nginx/html:/usr/share/nginx/html"depends_on:- mallnetworks:- mall-net
networks:mall-net:name: mall

可以看出来,compose只是整合了我们docker run中的参数。其中,depends_on指明了创建顺序。

我们只需要将相关的文件和Dockerfile和docker-compose.yml放到同一目录下,然后使用如下命令一键部署:

# 创建并启动所有容器(-f 指定composer文件位置,默认当前目录。-p 指定项目名。)
docker compose up -d
# 一键停止并移除所有容器、网络
docker compose down
# 查看容器进程状态
docker compose ps

以上利用composer实现了一键部署。DockerComposer的功能远不止于此,它可以借助DockerSwarm实现项目集群部署、多实例应用和负载均衡等。

在实际的项目开发中,程序员只需要编写好Dockerfile文件,会由专业的运维人员负责编写Composer实现集群和负载均衡的部署。

更进一步,在有些大企业中,程序员将后端代码push到仓库中后,会触发运维人员编写的自动化脚本对项目进行编译和部署。