1.Docker安装
1.如果Ubuntu自带的Docker版本太低,我们需要卸载旧版本并安装新的
sudo apt-get remove docker docker-engine docker.io containerd runc
2. 备份原有软件源
sudo cp /etc/apt/sources.list /etc/apt/sources.list.bak
3.选择合适的镜像源
# 或者使用清华大学sudo sed -i 's/http:\/\/archive\.ubuntu\.com/http:\/\/mirrors\.tuna\.tsinghua\.edu\.cn/' /etc/apt/sources.list
4.更新包信息
sudo apt updatesudo apt upgrade -y
5.安装Docker
接下来,添加Docker官方GPG密钥
sudo curl -fsSL http://mirrors.aliyun.com/docker-ce/linux/ubuntu/gpg | sudo apt-key add -
添加Docker官方软件源
sudo add-apt-repository "deb [arch=amd64] http://mirrors.aliyun.com/docker-ce/linux/ubuntu $(lsb_release -cs) stable"
然后安装Docker
sudo apt-get install -y docker-ce docker-ce-cli containerd.io
6.配置和启动Docker
检查Docker版本并启动Docker服务:
docker -v
sudo systemctl start docker
设置开机自启
sudo systemctl enable docker
2.Docker命令解析
2.1 Docker常用命令
命令 | 说明 | 文档地址 |
---|---|---|
docker pull | 拉取镜像 | docker pull |
docker push | 推送镜像到DockerRegistry | docker push |
docker images | 查看本地镜像 | docker images |
docker rmi | 删除本地镜像 | docker rmi |
docker run | 创建并运行容器(不能重复创建) | docker run |
docker stop | 停止指定容器 | docker stop |
docker start | 启动指定容器 | docker start |
docker restart | 重新启动容器 | docker restart |
docker rm | 删除指定容器 | docs.docker.com |
docker ps | 查看容器 | docker ps |
docker logs | 查看容器运行日志 | docker logs |
docker exec | 进入容器 | docker exec |
docker save | 保存镜像到本地压缩文件 | docker save |
docker load | 加载本地压缩文件到镜像 | docker load |
docker inspect | 查看容器详细信息 | docker inspect |
用一副图来表示这些命令的关系:
# Docker开机自启
systemctl enable docker# Docker容器开机自启
docker update --restart=always [容器名/容器id]
2.2 实践样例
以Nginx为例给演示上述命令:
# 第1步,去DockerHub查看nginx镜像仓库及相关信息# 第2步,拉取Nginx镜像 (比较耗时)
docker pull nginx:1.20.2# 第3步,查看镜像
docker images# 第4步,创建并允许Nginx容器
# 如果本地没有相应镜像,会直接从镜像仓库拉取镜像
docker run -d --name nginx -p 80:80 nginx# 第5步,查看运行中容器
docker ps# 也可以加格式化方式访问,格式会更加清爽
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"# 第6步,访问网页,地址:http://虚拟机地址# 第7步,停止容器
docker stop nginx# 第8步,查看所有容器
docker ps -a --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"# 第9步,再次启动nginx容器
docker start nginx# 第10步,再次查看容器
docker ps --format "table {{.ID}}\t{{.Image}}\t{{.Ports}}\t{{.Status}}\t{{.Names}}"# 第11步,查看容器详细信息
docker inspect nginx# 第12步,进入容器,查看容器内目录
docker exec -it nginx bash# 或者,可以进入MySQL
docker exec -it mysql mysql -uroot -p# 第13步,删除容器
docker rm nginx# 发现无法删除,因为容器运行中,强制删除容器
docker rm -f nginx
解析docker run命令:
docker run -d \--name mysql \-p 3307:3306 \-e TZ=Asia/Shanghai \-e MYSQL_ROOT_PASSWORD=123 \mysql:8
1. docker run :创建并运行一个容器
2. -d 是让容器在后台运行
3. --name mysql :给容器起个名字,必须唯一
4. -p 3307:3306 :设置端口映射,格式: -p 宿主机端口:容器内端口
,示例中就是将宿主机的3307映射到容器内的3306端口。容器是隔离环境,外界不可访问。但是可以将宿主机端口映射容器内到端口,当访问宿主机指定端口时,就是在访问容器内的端口了。
5. -e KEY=VALUE :是设置环境变量
6. mysql:8 :指定运行的镜像的名字,版本
镜像的概念:
镜像中不仅包含了软件本身,还包含了其运行所需要的环境、配置、系统级函数库。因此它在运行时就有自己独立的环境,就可以跨系统运行,也不需要手动再次配置环境了。这套独立运行的隔离环境我们称为容器。容器内部是一个完整的Linux 文件系统,容器运行的是镜像中自带的环境,不依赖宿主机的环境
镜像命名规范:
3.数据卷
3.1 数据卷介绍
数据卷(volume)是一个虚拟目录,是容器内目录与宿主机目录之间映射的桥梁。
以Nginx为例,我们知道Nginx中有两个关键的目录:
-
html
:放置一些静态资源 -
conf
:放置配置文件
如果我们要让Nginx代理我们的静态资源,最好是放到html
目录;如果我们要修改Nginx的配置,最好是找到conf
下的nginx.conf
文件。
但遗憾的是,容器运行的Nginx所有的文件都在容器内部。所以我们必须利用数据卷将两个目录与宿主机目录关联,方便我们操作。
在上图中:
-
我们创建了两个数据卷:
conf
、html
-
Nginx容器内部的
conf
目录和html
目录分别与两个数据卷关联。 -
而数据卷conf和html分别指向了宿主机的
/var/lib/docker/volumes/conf/_data
目录和/var/lib/docker/volumes/html/_data
目录
这样以来,容器内的conf
和html
目录就 与宿主机的conf
和html
目录关联起来,我们称为挂载。
此时,我们操作宿主机的/var/lib/docker/volumes/html/_data
就是在操作容器内的/usr/share/nginx/html
目录。只要我们将静态资源放入宿主机对应目录,就可以被Nginx代理了。
注:/var/lib/docker/volumes
这个目录就是默认的存放所有容器数据卷的目录,其下再根据数据卷名称创建新目录,格式为/数据卷名/_data
。
为什么不让容器目录直接指向宿主机目录呢?
-
因为直接指向宿主机目录就与宿主机强耦合了,如果切换了环境,宿主机目录就可能发生改变了。由于容器一旦创建,目录挂载就无法修改,这样容器就无法正常工作了。
-
但是容器指向数据卷,一个逻辑名称,而数据卷再指向宿主机目录,就不存在强耦合。如果宿主机目录发生改变,只要改变数据卷与宿主机目录之间的映射关系即可。不过,我们通过由于数据卷目录比较深,不好寻找,通常我们也允许让容器直接与宿主机目录挂载而不使用数据卷
3.2 数据卷命令
命令 | 说明 | 文档地址 |
---|---|---|
docker volume create | 创建数据卷 | docker volume create |
docker volume ls | 查看所有数据卷 | docs.docker.com |
docker volume rm | 删除指定数据卷 | docs.docker.com |
docker volume inspect | 查看某个数据卷的详情 | docs.docker.com |
docker volume prune | 清除数据卷 | docker volume prune |
注意:容器与数据卷的挂载要在创建容器时配置,对于创建好的容器,是不能设置数据卷的。而且创建容器的过程中,数据卷会自动创建。
演示一下nginx的html目录挂载:
# 1.首先创建容器并指定数据卷,注意通过 -v 参数来指定数据卷
docker run -d --name nginx -p 80:80 -v html:/usr/share/nginx/html nginx:1.20.2# 2.然后查看数据卷
docker volume ls# 3.查看数据卷详情
docker volume inspect html# 4.查看/var/lib/docker/volumes/html/_data目录
ll /var/lib/docker/volumes/html/_data# 5.进入该目录,并随意修改index.html内容
cd /var/lib/docker/volumes/html/_data
vi index.html# 6.打开页面,查看效果# 7.进入容器内部,查看/usr/share/nginx/html目录内的文件是否变化
docker exec -it nginx bash
-v 数据卷:容器内目录 用此形式可以完成数据卷挂载(数据卷不存在会自动创建)。宿主机目录固定:/var/lib/docker/volumes/数据卷名字/_data
挂载本地目录或文件:
可以发现,数据卷的目录结构较深,如果我们去操作数据卷目录会不太方便。在很多情况下,我们会直接将容器目录与宿主机指定目录挂载。挂载语法与数据卷类似:
# 挂载本地目录
-v 本地目录:容器内目录# 挂载本地文件
-v 本地文件:容器内文件
注意:本地目录或文件必须以 /
或 ./
开头,如果直接以名字开头,会被识别为数据卷名而非本地目录名。
-v mysql:/var/lib/mysql # 会被识别为一个数据卷叫mysql,运行时会自动创建这个数据卷-v ./mysql:/var/lib/mysql # 会被识别为当前目录下的mysql目录,运行时如果不存在会创建目录
删除并重新创建mysql容器,并完成本地目录挂载:
-
挂载
/root/mysql/data
到容器内的/var/lib/mysql
目录 -
挂载
/root/mysql/init
到容器内的/docker-entrypoint-initdb.d
目录(初始化的SQL脚本目录) -
挂载
/root/mysql/conf
到容器内的/etc/mysql/conf.d
目录(这个是MySQL配置文件目录)
docker run -d \
--name mysql \
-p 3307:3306 \
-e MYSQL_ROOT_PASSWORD=123 \
-e TZ=Asia/Shanghai \
-v /root/mysql/data:/var/lib/mysql \
-v /root/mysql/init:/docker-entrypoint-initdb.d \
-v /root/mysql/conf:/etc/mysql/conf.d \
mysql:8
4. 自定义镜像
前面我们一直在使用别人准备好的镜像,那如果我要部署一个Java项目,把它打包为一个镜像该怎么做呢? 那接下来,我们就来介绍一下如何自定义镜像。
4.1 镜像结构
镜像之所以能让我们快速跨操作系统部署应用而忽略其运行环境、配置,就是因为镜像中包含了程序运行需要的系统函数库、环境、配置、依赖。因此,自定义镜像本质就是依次准备好程序运行的基础环境、依赖、应用本身、运行配置等文件,并且打包而成。
我们要从0部署一个Java应用,大概流程是这样:
-
准备一个linux服务(CentOS或者Ubuntu均可)
-
安装并配置JDK
-
上传Jar包
-
运行jar包
那因此,我们打包镜像也是分成这么几步:
-
准备Linux运行环境(java项目并不需要完整的操作系统,仅仅是基础运行环境即可)
-
安装并配置JDK
-
拷贝jar包
-
配置启动脚本
上述步骤中的每一次操作其实都是在生产一些文件(系统运行环境、函数库、配置最终都是磁盘文件),所以镜像就是一堆文件的集合。
但需要注意的是,镜像文件不是随意堆放的,而是按照操作的步骤分层叠加而成,每一层形成的文件都会单独打包并标记一个唯一id,称为Layer(层)。这样,如果我们构建时用到的某些层其他人已经制作过,就可以直接拷贝使用这些层,而不用重复制作。
例如,第一步中需要的Linux运行环境,通用性就很强,所以Docker官方就制作了这样的只包含Linux运行环境的镜像。我们在制作java镜像时,就无需重复制作,直接使用Docker官方提供的CentOS或Ubuntu镜像作为基础镜像。然后再搭建其它层即可,这样逐层搭建,最终整个Java项目的镜像结构如图所示:
4.2 Dockerfile
由于制作镜像的过程中,需要逐层处理和打包,比较复杂,所以Docker就提供了自动打包镜像的功能。我们只需要将打包的过程,每一层要做的事情用固定的语法写下来,交给Docker去执行即可。而这种记录镜像结构的文件就称为Dockerfile。
说明 | 示例 |
---|---|
指定基础镜像 |
|
设置环境变量,可在后面指令使用 |
|
拷贝本地文件到镜像的指定目录 |
|
执行Linux的shell命令,一般是安装过程的命令 |
|
指定容器运行时监听的端口,是给镜像使用者看的 | EXPOSE 8080 |
镜像中应用的启动命令,容器运行时调用 | ENTRYPOINT java -jar xx.jar |
例如,要基于 centos:7 镜像来构建一个Java应用,其Dockerfile内容如下:
# 使用 CentOS 7 作为基础镜像
FROM centos:7# 添加 JDK 到镜像中
COPY jdk17.tar.gz /usr/local/
RUN tar -xzf /usr/local/jdk17.tar.gz -C /usr/local/ && rm /usr/local/jdk17.tar.gz# 设置环境变量
ENV JAVA_HOME=/usr/local/jdk-17.0.10
ENV PATH=$JAVA_HOME/bin:$PATH# 创建应用目录
RUN mkdir -p /app
WORKDIR /app# 复制应用 JAR 文件到容器
COPY app.jar app.jar# 暴露端口
EXPOSE 8080# 运行命令
ENTRYPOINT ["java","-Djava.security.egd=file:/dev/./urandom","-jar","/app/app.jar"]
Dockerfile文件编写好了之后,就可以使用如下命令来构建镜像了。
docker build -t 镜像名 .
-
-t :是给镜像起名,格式依然是repository:tag的格式,不指定tag时,默认为latest
-
. :是指定Dockerfile所在目录,如果就在当前目录,则指定为"."
5. 网络
首先,我们查看下MySQL容器的详细信息,重点关注其中的网络IP地址:
# 1.用基本命令,寻找Networks.bridge.IPAddress属性
docker inspect mysql# 也可以使用format过滤结果
docker inspect --format='{{range .NetworkSettings.Networks}}{{println .IPAddress}}{{end}}' mysql# 得到IP地址如下:
172.17.0.2# 2.然后通过命令进入dd容器
docker exec -it dd bash# 3.在容器内,通过ping命令测试网络
ping 172.17.0.2# 结果
PING 172.17.0.2 (172.17.0.2) 56(84) bytes of data.
64 bytes from 172.17.0.2: icmp_seq=1 ttl=64 time=0.053 ms
64 bytes from 172.17.0.2: icmp_seq=2 ttl=64 time=0.059 ms
64 bytes from 172.17.0.2: icmp_seq=3 ttl=64 time=0.058 ms
发现可以互联,没有问题。
但是,容器的网络IP其实是一个虚拟的IP,其值并不固定与某一个容器绑定,如果我们在开发时写死某个IP,而在部署时很可能MySQL容器的IP会发生变化,连接会失败。
常见命令有:
命令 | 说明 |
---|---|
docker network create | 创建一个网络 |
docker network ls | 查看所有网络 |
docker network rm | 删除指定网络 |
docker network prune | 清除未使用的网络 |
docker network connect | 使指定容器连接加入某网络 |
docker network disconnect | 使指定容器连接离开某网络 |
docker network inspect | 查看网络详细信息 |
自定义网络:
# 1.首先通过命令创建一个网络
docker network create itheima# 2.然后查看网络
docker network ls# 结果:
NETWORK ID NAME DRIVER SCOPE
639bc44d0a87 bridge bridge local
403f16ec62a2 itheima bridge local
0dc0f72a0fbb host host local
cd8d3e8df47b none null local
# 其中,除了itheima以外,其它都是默认的网络# 3.让 myapp 和 mysql 都加入该网络
# 3.1.mysql容器,加入 itheima 网络
docker network connect itheima mysql# 3.2.myapp容器,也就是我们的java项目, 加入 itheima 网络
docker network connect itheima myapp# 4.进入dd容器,尝试利用别名访问db
# 4.1.进入容器
docker exec -it myapp bash# 4.2.用容器名访问
ping mysql# 结果:
PING mysql (172.18.0.2) 56(84) bytes of data.
64 bytes from mysql.itheima (172.18.0.2): icmp_seq=1 ttl=64 time=0.044 ms
64 bytes from mysql.itheima (172.18.0.2): icmp_seq=2 ttl=64 time=0.054 ms
现在无需记住IP地址也可以实现容器互联了。
6.Docker项目部署
我们将一个SpringBoot测试项目打包为镜像并部署
1.项目配置文件
2.项目打成jar包
3.编写Dockerfile文件
# 第一阶段:使用 Maven 镜像构建项目
FROM maven:3.8.4-openjdk-17 AS builder# 设置工作目录
WORKDIR /app# 复制项目的 pom.xml 和 src 目录到容器中
COPY pom.xml .
COPY src ./src# 使用 Maven 构建项目并跳过测试
RUN mvn clean package -DskipTests# 第二阶段:使用 Ubuntu 镜像运行应用
FROM ubuntu:20.04# 安装必要的工具(如 curl 用于健康检查等)
RUN apt-get update && apt-get install -y \curl \&& rm -rf /var/lib/apt/lists/*# 设置工作目录
WORKDIR /app# 从 builder 阶段复制构建好的 JAR 文件到当前阶段
COPY --from=builder /app/target/*.jar app.jar# 暴露端口 8080
EXPOSE 8080# 容器启动时执行的命令
ENTRYPOINT ["java", "-jar", "app.jar"]
4.服务器配置JDK环境并将jar包和Dockerfile文件上传到服务器项目路径下
5.构建镜像
docker build -t test:1.0 .
6.部署容器
docker run -d
--name test-server
-p 8080:8080
test:1.0
通过 docker logs -f 容器名
,就可以查看容器的运行日志。
这样后端服务,就已经启动起来了。
7.DockerCompose
家可以看到,我们部署一个简单的java项目,其中包含3个容器:
-
MySQL
-
Nginx
-
Java项目
而稍微复杂的项目,其中还会有各种各样的其它中间件,需要部署的东西远不止3个。如果还像之前那样手动的逐一部署,就太麻烦了。而Docker Compose就可以帮助我们实现多个相互关联的Docker容器的快速部署。它允许用户通过一个单独的 docker-compose.yml 模板文件(YAML 格式)来定义一组相关联的应用容器。
基本语法:
docker-compose文件中可以定义多个相互关联的应用容器,每一个应用容器被称为一个服务(service)。由于service就是在定义某个应用的运行时参数,因此与docker run
参数非常相似。
举例来说,用docker run部署MySQL的命令如下:
docker run -d \
--name nginx-tlias \
-p 80:80 \
-v /usr/local/app/html:/usr/share/nginx/html \
-v /usr/local/app/conf/nginx.conf:/etc/nginx/nginx.conf \
--network itheima \
nginx:1.20.2
如果用docker-compose.yml
文件来定义,就是这样:
services:mysql:image: "nginx:1.20.2"container_name: nginx-tliasports:- "80:80"volumes:- "/usr/local/app/html:/usr/share/nginx/html"- "/usr/local/app/conf/nginx.conf:/etc/nginx/nginx.conf"networks:- itheima
networks:itheima:name: itheima
对比如下:
docker run 参数 | docker compose 指令 | 说明 |
---|---|---|
--name | container_name | 容器名称 |
-p | ports | 端口映射 |
-e | environment | 环境变量 |
-v | volumes | 数据卷配置 |
--network | networks | 网络 |
services:mysql:image: mysql:8container_name: mysqlports:- "3307:3306"environment:TZ: Asia/ShanghaiMYSQL_ROOT_PASSWORD: 123volumes:- "/usr/local/app/mysql/conf:/etc/mysql/conf.d"- "/usr/local/app/mysql/data:/var/lib/mysql"- "/usr/local/app/mysql/init:/docker-entrypoint-initdb.d"networks:- tlias-nettlias:build: context: .dockerfile: Dockerfilecontainer_name: tlias-serverports:- "8080:8080"networks:- tlias-netdepends_on:- mysqlnginx:image: nginx:1.20.2container_name: nginxports:- "80:80"volumes:- "/usr/local/app/nginx/conf/nginx.conf:/etc/nginx/nginx.conf"- "/usr/local/app/nginx/html:/usr/share/nginx/html"depends_on:- tliasnetworks:- tlias-net
networks:tlias-net:name: itheima
基础命令:
编写好docker-compose.yml文件,就可以部署项目了。语法如下:
docker compose [OPTIONS] [COMMAND]
其中,OPTIONS和COMMAND都是可选参数,比较常见的有:
类型 | 参数或指令 | 说明 |
---|---|---|
Options | -f | 指定compose文件的路径和名称 |
-p | 指定project名称。project就是当前compose文件中设置的多个service的集合,是逻辑概念 | |
Commands | up | 创建并启动所有service容器 |
down | 停止并移除所有容器、网络 | |
ps | 列出所有启动的容器 | |
logs | 查看指定容器的日志 | |
stop | 停止容器 | |
start | 启动容器 | |
restart | 重启容器 | |
top | 查看运行的进程 | |
exec | 在指定的运行中容器中执行命令 |