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 update sudo 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
Docker镜像配置:
镜像地址可能会变更,如果失效可以百度找最新的docker镜像。
配置镜像步骤如下:
# 创建目录 mkdir -p /etc/docker # 复制内容 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
阿里云镜像:
开通阿里云镜像服务:
编辑
执行命令:
# 创建目录 mkdir -p /etc/docker # 复制内容,注意把其中的镜像加速地址改成你自己的 tee /etc/docker/daemon.json <<-'EOF' { "registry-mirrors": ["https://xxxx.mirror.aliyuncs.com"] } EOF # 重新加载配置 systemctl daemon-reload # 重启Docker systemctl restart docker
2.Docker命令解析
2.1 Docker常用命令
命令 |
说明 |
文档地址 |
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 |
查看容器详细信息 |
用一副图来表示这些命令的关系:
编辑
# 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 ls |
查看所有数据卷 |
|
docker volume rm |
删除指定数据卷 |
|
docker volume inspect |
查看某个数据卷的详情 |
|
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-tlias ports: - "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:8 container_name: mysql ports: - "3307:3306" environment: TZ: Asia/Shanghai MYSQL_ROOT_PASSWORD: 123 volumes: - "/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-net tlias: build: context: . dockerfile: Dockerfile container_name: tlias-server ports: - "8080:8080" networks: - tlias-net depends_on: - mysql nginx: image: nginx:1.20.2 container_name: nginx ports: - "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: - tlias networks: - 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 |
在指定的运行中容器中执行命令 |