本文由作者的学习笔记和经验转化而来,共计13458字,预计阅读时间41分钟;包含了docker基础知识和实战指南
如果只是想用docker快速部署项目,可以直接跳转到"实战-使用Docker compose部署"

整理不易,给我点个赞吧 o( ̄▽ ̄)d

  • Docker的作用
    • 忽略了安装的复杂过程
    • 忽略了系统间的差异
    • 忽略了系统和应用之间的干扰
    • 大幅提高部署效率

镜像和容器

当我们利用Docker安装应用时,Docker会自动搜索并下载应用镜像。镜像不仅包含应用本身,还包含应用运行所需要的环境、配置、系统函数库。Docker会在运行镜像时创建一个隔离环境,称为容器
镜像仓库:存储和管理镜像的平台(Docker官方维护的镜像仓库是DockerHub

一个镜像不依赖于操作系统,不仅可以在linux上用,还可以在其他系统上用

【镜像把它所依赖的系统环境及其函数库也一起打包了,所以镜像中的应用可以在各种操作系统直接运行】

各个应用所需的环境、函数库往往都不一样,通过容器这种隔离环境的机制,让各个应用各自运行、相互不干扰

镜像只用下载一次,一个镜像可以多开(可以开启多次)、不需要重复下载

如何获取到镜像?

  • 从docker/国内公司 维护的镜像仓库,就能获取到想要的镜像(或者公司也可以搭建自己的镜像仓库的私服(类似maven私服))

DockerHub——该网站作用:方便我们查阅里面已经保存的镜像和版本号】

  • docker安装好之后,系统就会启动一个守护进程(用来处理客户端发过来的一些请求[比如docker run ]),监听到请求之后就会从镜像仓库找到这个镜像
  • 然后将这个镜像(image)下载到本地
  • docker可以基于一个镜像构建出多个容器(多个容器之间相互隔离互不影响),各个容器之中可以独立的运行不同的软件、相互独立互不影响(每个容器有独立的网络环境、ip地址等)

执行指令的时候,docker先会从本地找镜像、本地没有才从镜像仓库下载

容器id:容器的唯一标识

Docker基础

Docker安装

不同系统的安装流程推荐查看官方文档

(centos版本流程如下)

  1. 移除系统中的旧版本Docker

    text
    yum remove docker \
    	docker-client \
    	docker-client-latest \
    	docker-common \
    	docker-latest \
    	docker-latest-logrotate \
    	docker-engine \
    	docker-selinux
    
  2. 配置Docker的yum库

    首先安装一个yum工具

    text
    sudo yum install -y yum-utils device-mapper-persistent-data lvm2
    

    安装成功后,执行命令,配置Docker的yum源(已更新为阿里云源)

    text
    sudo yum-config-manager --add-repo https://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo
    
    sudo sed -i 's+download.docker.com+mirrors.aliyun.com/docker-ce+' /etc/yum.repos.d/docker-ce.repo
    

    更新yum,建立缓存

    Bash
    sudo yum makecache fast
    
  3. 安装Docker

    执行命令、安装Docker

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

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

    镜像地址可能会变更,如果失效可以百度找最新的docker镜像。

    配置镜像步骤如下:

    text
    # 创建目录
    rm -f /etc/docker/daemon.json
                      
    # 复制内容
    tee /etc/docker/daemon.json <<-'EOF'
    {
        "registry-mirrors": [
            "http://hub-mirror.c.163.com",
            "https://mirrors.tuna.tsinghua.edu.cn",
            "http://mirrors.sohu.com",
            "https://ustc-edu-cn.mirror.aliyuncs.com",
            "https://ccr.ccs.tencentyun.com",
            "https://docker.m.daocloud.io",
            "https://docker.awsl9527.cn"
        ]
    }
    EOF
                      
    # 重新加载配置
    systemctl daemon-reload
                      
    # 重启Docker
    systemctl restart docker
    

配置阿里镜像加速

  • 具体流程参照官网的操作文档即可

    完成流程之后可以用docker run hello-world测试,此时下载镜像就很快了

常见命令

  1. 拉取镜像, 以nginx为例

    text
    # docker pull 镜像名:版本号
    docker pull nginx:1.20.2
    
  2. 查看本地镜像列表

    text
    docker images
    

    删除镜像

    text
    docker rmi nginx:1.20.2
    

    跟名称/id

  3. 创建并运行容器

    text
    docker run -d --name nginx -p 80:80 nginx:1.20.2
    

    nginx不用环境变量,所以没加-e参数

    注意:docker run是创建并运行,每次执行都会创建一个新的容器(注意判断是否有必要使用,可能容器只是stop了,没必要再创建一个)

  4. 查看容器(查看和容器相关的进程)

    text
    docker ps
    

    默认只查看运行中的容器 推荐加上 -a

    查看所有容器

    text
    docker ps -a
    
  5. 停止容器

    text
    docker stop nginx
    
  6. 再次启动容器

    text
    docker start nginx
    
  7. 进入容器

    text
    docker exec -it nginx bash
    

    bash:指通过命令行和容器交互,进入nginx容器之后,会打开一个bash shell命令行进行交互

    退出容器

    text
    exit
    
  8. 删除容器

    text
    docker stop nginx
    docker rm nginx
    

    注意和docker rm 区分34

如何将镜像打包并发给别人:

text
docker save -o nginx-1.20.2.tar nginx:1.20.2

打包之后下载到本地就可以发给别人

text
docker load -i nginx-1.20.2.tar

其他方式

把(可能是自己构建的)镜像
使用docker push推到镜像仓库(公用/公司的私服)

  • 其他

    • 保存镜像

      text
      # docker save -o 保存到的文件名 镜像名:版本
      docker save -o nginx.tar nginx:latest
      
    • 读取本地的镜像

      text
      # docker load -i 要读取的文件名
      docker load -i nginx.tar
      
    • 查看容器日志

      text
      # docker logs 镜像名
      docker logs nginx
      
    • 进入mysql容器里面的mysql

      text
      docker exec -it mysql mysql -uroot -p
      
      

不清楚某个指令可以加上 “–help”查看帮助文档

text
docker save --help

docker run 命令解读

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

docker run :创建并运行一个容器,-d是让容器在后台运行
–name musql :给容器起个名字,必须唯一
-p 3307:3306:设置端口映射
-e KEY=VALUE:设置环境变量

端口映射

宿主机的端口:容器的端口

当外部访问宿主机的3307端口的时候,dokcer就会把这个请求转到容器内的3306端口

如果有两台mysql容器,就要映射到宿主机的不同端口(宿主机只有一个3306端口)

不同镜像,需要配置的环境变量不一样、是由官方决定的(需要去官网查询需要/必配置的 环境变量)

在hub.docker.com找到对应的镜像,然后找到他需要配置的环境变量

最后的mysql是镜像的名字,搜索部署和下载都是靠这个名字来指定的

text
# 查看指定容器的信息
docker inspect containerName

镜像名称一般由两部分组成:
[repository]:[tag]
即 镜像名:版本号
如果不填版本号,默认最新版

运行容器一条龙

  • DockerHub中搜索xx镜像,查看镜像名称

  • 拉取xx镜像

    text
    git pull 镜像名:版本号
    或
    git pull 镜像名
    
  • 查看本地镜像列表(是否拉取成功)

    text
    docker images
    
  • 查看镜像需要的环境变量

  • 创建并运行x容器(注意设置容器名、端口映射、配置环境变量)

    text
    docker -run -d --name 镜像名 -p 宿主机端口:容器端口 -e 环境变量1=xx 容器名
    
  • 查看容器(容器是否创建成功)

    text
    docker ps -a
    

技巧

设置 指令别名

  • 案例

    设置简化输出进程信息的指令

  • 进入 ~/.bashrc

    text
    vi ~/.bashrc
    
  • 设置别名

    text
    alias dps='docker ps --format "table {{.ID}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}" -a'
    
  • 让别名生效

    text
    source ~/.bashrc
    

数据卷

案例需求:

  • 创建Nginx容器,修改nginx容器内的html目录下的index.html文件,查看变化
  • 将静态资源部署到nginx的html目录
  • 但是所有镜像都是最小化的系统环境,它只具备运行所必须的环境
    所以在容器内修改文件、拷贝文件很困难

所以需要借助数据卷来操作

  • 数据卷是虚拟的,但是宿主机会自动在指定目录下创建对应的和数据卷绑定的目录,数据卷又和容器有关联;从而让容器和宿主机映射

    双向绑定,一边变 另一边也变

  • 指令一览

    • docker volume create 创建数据卷
    • docker volume ls 查看所有数据卷
    • docker volume rm 数据卷名 ——删除指定数据卷
    • docker volume inspect 数据卷名 ——查看某个数据卷的详情
    • docker volume prune ——清楚所有未使用的数据卷
    text
    docker volume --help
    

创建容器的同时才能挂载数据卷,如果容器已经创建了 ,就没办法挂载数据卷了

在执行docker run 命令时,使用-v 数据卷:容器内目录 形式,就可以完成数据卷挂载
具体挂载的容器内目录去官网看

  • 案例需求解决

    text
    docker run -d --name nginx -p 80:80 -v html:/usr/share/nginx/html nginx
    

    这里的html是数据卷名字,冒号后是容器内目录

    • 通过docker volume ls可以查看已经挂载的数据卷

指令-展示数据卷的详细信息

text
docker volume inspect 数据卷名字

包括映射到的文件位置

本地目录挂载(自定义挂载位置)

匿名数据卷:

起到数据备份的作用
容器自带;
缺点:名字长;挂载位置深;不方便迁移

建议挂载到固定的、自己设置的目录

命令

text
docker run -d --name 容器名 -p 宿主机端口:容器端口 -v 宿主机目录或文件:容器内目录或文件 镜像名

注意
本地目录必须 以 / 或 ./ 开头,如果直接以名称开头,会被识别为数据卷而非本地目录
-v mysql:/var/lib/mysql 会被识别为一个数据卷,数据卷叫mysql
-v ./mysql:/var/lib/mysql 会被识别为当前目录下的mysql目录

案例

本地目录挂载实现 MySQL容器数据目录、配置文件、初始化脚本的目录挂载

text
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自定义镜像

自己自定义自己java程序的镜像

  • 镜像包含了应用程序、程序运行的系统函数库、运行配置等文件的文件包。构建镜像的过程其实就是把上述文件打包的过程

dockerFile

Dockerfile是个包含指令的文本文件,用指令来说明要执行什么从操作来构建镜像,让Docker可以根据Dockerfile帮助我们自动构建镜像

相当于封装了我们人工部署JAR包的过程
利用固定的指令来描述镜像的结构和构建过程,让docker依次构建镜像
我们只需要描述好镜像的结构,基础是xx,中间层有哪些层,docker就会自动帮我们完成整个镜像的构建了

常见命令

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

更全的语法看官方文档

案例

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

-t:给镜像起名
. :指定Dockerfile所在目录,如果就在当前目录,则指定为“.”

注意Dockerfile放置的目录要和你拷贝操作设置的逻辑匹配,此处要和被拷贝的那个jar包放一起

  • 准备Dockerfile和jar包

  • 下载所需的jdk基础镜像

  • 进入到Dockerfile所在目录

  • 执行指令,部署容器

    text
    # docker build -t 构建出的镜像的名字 .
    docker build -t docker-demo .
    

    指令没有指定的话,Dockerfile文件名就要叫Dockerfile

    这里的“.”指定了Dockerfile的路径

    检查是否添加上了

    text
    docker images
    

    创建并运行容器

    text
    docker run -d --name dockerName -p 8080:8080 docker-demo
    

容器网络互连

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

这种通过ip相互连接的方式不太好

ip会变,如果另外一个容器先启动、占掉了mysql的之前的ip地址,mysql容器再启动之后ip地址就变化了

优化:自定义网络
常用指令

  • docker network create:创建一个网络
  • docker network ls:查看所有网络
  • docker network rm :删除制定网络
  • docker network prune:清除未使用的网络
  • docker network connect:使指定容器加入某网络
  • docker network disconnect:使指定容器连接离开某网络
  • docker network inspect:查看网络详细信息

加入自定义网络之中的容器 可以通过容器名互相访问,不需要知道对方的ip地址

  • 如果有同一个网关,就处于同一个网段

同一个网段中的容器是可以互相访问的

  • 指令使用

    创建自定义网段

    text
    docker network create heima
    

    查看当前的网段

    text
    docker network ls
    

    加入容器

    text
    # docker network connect 网络名 容器名
    docker network connect heima mysql
    

    或者在创建容器的同时加入网段

    text
    docker run -d --name dd -p 8080:8080 -network heima docker-demo
    

DockerCompose部署项目

Docker Compose通过一个单独的docker-compose.yml模版文件来定义一组相关联的应用容器,帮我们实现多个互相关联的Docker容器的快速部署

描述了项目内多个容器及其参数信息

  • 运行的时候自动读取这个文件
    就能按照这个文件的描述自动创建和运行所需要的容器

语法案例

text
version: "3.8"

services:
  mysql:
    image: mysql
    container_name: mysql
    ports:
      - "3306:3306"
    environment:
      TZ: Asia/Shanghai
      MYSQL_ROOT_PASSWORD: yourPassword
    volumes:
      - "./mysql/conf:/etc/mysql/conf.d"
      - "./mysql/data:/var/lib/mysql"
      - "./mysql/init:/docker-entrypoint-initdb.d"
    networks:
      - example-net
  example:
    build:
      context: .
      dockerfile: ./Dockerfile
    container_name: blog
    ports:
      - "8080:8080"
    networks:
      - example-net
    depends_on:
      - mysql
  nginx:
    image: nginx
    container_name: nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "./nginx/nginx.conf:/etc/nginx/nginx.conf"
      - "./nginx/html:/usr/share/nginx/html"
      - "./nginx/ssl:/etc/nginx/ssl/:ro"
    depends_on:
      - example
    networks:
      - example-net
networks:
  example-net:
    name: example

用docker compose之后,java应用不再需要我们 手动通过docker build构建成镜像,再创建运行容器,网络也不需要自己创建网络,直接在文件里面描述

这里配置了“build: ” “路径”和“Docker“,它就会按照这个路径读取Dockerfile,

备注:按照案例中的路径
docker-compose.yml要和Dockerfile、mysql、nginx、jar包放在一起

  • 接着使用docker compose命令构建即可顺利部署
text
docker compose up -d

实战-使用Docker compose部署

编写docker-compose.yml文件

text
version: "3.8"

services:
  mysql:
    image: mysql
    container_name: mysql
    ports:
      - "3306:3306"
    environment:
      TZ: Asia/Shanghai
      MYSQL_ROOT_PASSWORD: yourPassword
    volumes:
      - "./mysql/conf:/etc/mysql/conf.d"
      - "./mysql/data:/var/lib/mysql"
      - "./mysql/init:/docker-entrypoint-initdb.d"
    networks:
      - example-net
  example:
    build:
      context: .
      dockerfile: ./Dockerfile
    container_name: example
    ports:
      - "8080:8080"
    networks:
      - example-net
    depends_on:
      - mysql
  nginx:
    image: nginx
    container_name: nginx
    ports:
      - "80:80"
      - "443:443"
    volumes:
      - "./nginx/nginx.conf:/etc/nginx/nginx.conf"
      - "./nginx/html:/usr/share/nginx/html"
      - "./nginx/ssl:/etc/nginx/ssl/:ro"
    depends_on:
      - example
    networks:
      - example-net
networks:
  example-net:
    name: example

version 字段是必需的,而且必须是有效的 Compose 文件版本号(如 "3.8""3" 等)。

✅ 建议使用较新的版本,比如 "3.8""3.7"

example容器的内部端口映射应该和你的jar包设置的端口一致(比如 如果java应用用的8080端口,这里内部端口就要设置成8080)

docker-compose.yml的数据库密码要和配置文件一样,不然连接不上

准备好application-prod.yml文件

  • 调整host,改为mysql的容器名
  • 调整云服务器mysql密码 调整云服务器redis密码
  • (修改一些clientId/client-secret 之类的需要配置的东西)

真实项目部署中,数据库不会设置端口映射,都是通过容器名访问 所以后端的jdbc连接要按容器名配置

  • 由激活application-dev.yml改成激活application-prod.yml

后端

编写Dockerfile文件

text
# 基础镜像
FROM openjdk:8-jre-slim
# 设定时区
ENV TZ=Asia/Shanghai
RUN ln -snf /usr/share/zoneinfo/$TZ /etc/localtime && echo $TZ > /etc/timezone

# 拷贝jar包
# COPY blog-server-1.0-SNAPSHOT.jar /app.jar
COPY blog-server-*.jar /app.jar

# 入口
ENTRYPOINT ["java","-jar","/app.jar"]

基础镜像视自己项目而定

使用maven的package打包项目成jar包

  • 找到父工程
  • Lifecysle-package
  • jar包在Service模块中的target目录下:blog-service.jar

准备mysql相关文件

进入root目录,然后创建目录

text
cd /root/
mkdir -p mysql/conf mysql/data mysql/init

/conf

放置 .cnf 文件来覆盖或添加 MySQL 的配置项;可能包括字符集设置、缓存大小调整、日志设置等

text
[client]
default_character_set=utf8mb4
[mysql]
default_character_set=utf8mb4
[mysqld]
character_set_server=utf8mb4
collation_server=utf8mb4_0900_ai_ci
init_connect='SET NAMES utf8mb4'

/data

MySQL 数据库的主要数据存储位置。

  • 此目录存放所有 MySQL 数据库的数据文件。当你的数据库中创建表、插入数据等操作时,这些更改都会被保存到此目录下的文件中。
  • 通过将这个目录映射到宿主机,即使容器被删除或重新创建,你的数据仍然安全地保留在宿主机上。

/init

用于初始化数据库。

  • 在这个目录下,你可以放置一些 SQL 脚本或者 shell 脚本,这些脚本会在 MySQL 容器启动并且没有检测到现有的数据库时自动执行。
  • 常见的做法是放置 .sql 文件来创建数据库、表或插入初始数据;也可以放置 .sh 脚本来进行更复杂的初始化操作。

准备nginx相关文件

创建目录

text
mkdir -p nginx/html

编写.env.production文件

原来的.env.devlopment文件不用删除

Vite 在 不同命令下会自动加载不同的 .env 文件

打包项目

  • 我们开发用的脚手架其实就是一个微型服务器,用于:支撑开发环境,运行代理服务器等

  • 打包完的文件中不存在:.vue、.jsx、.less等文件,而是:html、css、js等浏览器能识别的文件

  • 打包后的文件不借助脚手架运行,而是需要部署到服务器上运行

  • 打包前,先梳理好前端项目的ajax封装(请求前缀、代理规则等);

text
 npm run build

打包后会生成dist/build文件夹

/html

目录用于存放 前端静态资源文件,也就是用户通过浏览器访问你的博客时,Nginx 直接返回的 HTML、CSS、JavaScript、图片等内容。

text
npm run build

然后把 dist/ 里的所有文件复制到部署目录的 nginx/html

最终结构类似

text
nginx/html/
├── index.html
├── assets/
│   ├── index-abc123.js
│   ├── index-def456.css
│   └── logo.woff2
├── favicon.ico
└── robots.txt

编写nginx.conf

text


worker_processes  1;

events {
    worker_connections  1024;
}

http {
    include       mime.types;
    default_type  application/json;

    sendfile        on;
    
    keepalive_timeout  65;

        # ========== HTTP 重定向到 HTTPS ==========
    server {
        listen 80;
        server_name yourServerName1 yourServerName2 ...;

        # 所有 HTTP 请求 301 永久重定向到 HTTPS
        return 301 https://$host$request_uri;
    }

    # ========== HTTPS 服务 ==========
    server {
        listen 443 ssl;
        server_name yourServerName1 yourServerName2 ...;

        # SSL 证书和私钥路径(根据你的实际路径修改)
        ssl_certificate      /etc/nginx/ssl/example.pem;     # 证书链(含中间证书)
        ssl_certificate_key  /etc/nginx/ssl/example.key;       # 私钥文件


        # 前端静态资源
        location / {
            root /usr/share/nginx/html;
            try_files $uri $uri/ /index.html;
        }

        # 错误页面
        error_page   500 502 503 504  /50x.html;
        location = /50x.html {
            root html;
        }

        # API 代理
        location /api/ {
            proxy_pass http://example:8080/;  # 注意结尾的 /
            proxy_set_header Host $host;
            proxy_set_header X-Real-IP $remote_addr;
            proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;
            proxy_set_header X-Forwarded-Proto $scheme;
        }
    }
    
}

转发到的容器名和端口和你的实际情况匹配

运行docker-compose.yml

进入文件所在目录,执行

text
docker compose up -d

查看项目下的所有进程

text
docker compose ps

如果有失误,

比如mysql初始化失误

text
# 1. 停止并删除容器
docker compose down

# 2. 删除旧的 MySQL 数据(⚠️ 会丢失所有数据!)
sudo rm -rf ./mysql/data/*

# 3. (可选)确认 init 目录下是你新改好的 SQL 文件
ls ./mysql/init/

# 4. 重新启动
docker compose up -d

如果jar包有问题

text
# 换jar包

docker compose down
docker compose up -d --build # --build 会强制重新构建镜像,确保用的是新 JAR。

给云服务器添加入方向规则

到这里就差不多了,如果中途遇到其他bug,可以问问ai,更全面的部署上线流程可以看这篇文章