Docker
Docker
CalyeeDocker安装
卸载
如果之前有安装过旧版本的Docker,则通过命令卸载旧版本的Docker
1 | yum remove docker \ |
没有任何匹配,说明没装过docker
安装
首先需要虚拟机联网,安装yum工具
1 | yum install -y yum-utils \ |
运行耐心等待一下,安装完成(最后显示 完毕!)
然后更新本地镜像源:配置阿里云的仓库
1 | # 设置docker镜像源 |
安装docker
1 | yum install -y docker-ce |
docker-ce: 社区版
启动Docker
防火墙设置
1 | # 关闭 |
启动
1 | systemctl start docker # 启动docker服务 |
查看docker状态
1 | systemctl status docker |
查看docker版本
1 | docker -v |
配置镜像加速
docker官方镜像仓库网速较差,我们需要设置国内镜像服务:
参考阿里云的镜像加速文档:https://cr.console.aliyun.com/cn-hangzhou/instances/mirrors
1 | sudo mkdir -p /etc/docker |
部署MySQL
先停掉虚拟机中的MySQL,确保虚拟机已经安装Docker,且网络开通的情况下,执行下面命令即可安装MySQL
1 | docker run -d \ |
解读:
docker run -d :创建并运行一个容器,**-d**则是让容器以后台进程运行
–name mysql : 给容器起个名字叫mysql,必须唯一
-p 3306:3306 : 设置端口映射。(后面这个是容器内MySQL默认端口,前面是映射出去的端口(宿主机),比如3307)
容器是隔离环境,外界不可访问。但是可以将宿主机端口映射容器内到端口,当访问宿主机指定端口时,就是在访问容器内的端口了。
容器内端口往往是由容器内的进程决定,例如MySQL进程默认端口是3306,因此容器内端口一定是3306;而宿主机端口则可以任意指定,一般与容器内保持一致。
格式: -p 宿主机端口:容器内端口,示例中就是将宿主机的3306映射到容器内的3306端口
-e TZ=Asia/Shanghai : 配置容器内进程运行时的一些参数(e–>environment)
格式:**-e KEY=VALUE**,KEY和VALUE都由容器内进程决定
案例中,TZ=Asia/Shanghai是设置时区;MYSQL_ROOT_PASSWORD=123456是设置MySQL默认密码
mysql : 设置镜像名称,Docker会根据这个名字搜索并下载镜像
格式:REPOSITORY:TAG,例如mysql:8.0,其中REPOSITORY可以理解为镜像名,TAG是版本号
在未指定TAG的情况下,默认是最新版本,也就是mysql:latest
连接远程数据库
打开数据库连接工具
如果出现没有权限,则在Docker容器中,输入指令,进入容器,查看容器内目录
1 | docker exec -it container_name /bin/bash |
登录MySQL
1 | mysql -u root -p |
修改权限
1 | ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456'; |
刷新权限
1 | FLUSH PRIVILEGES; #刷新权限 |
最后查询用户信息 (查看root用户)
1 | select host,user,plugin,authentication_string from mysql.user; |
连接成功则可以像在Windows操作本地数据库一样的了
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 stats | 查看容器资源使用情况(通过容器id) |
Docker文档Docker Docs
案例
以拉取Nginx为例:
需求:
- 在DockerHub中搜索Nginx镜像,查看镜像的名称
- 拉取Nginx镜像
- 查看本地镜像列表
- 创建并运行Nginx容器
- 查看容器
- 停止容器
- 再次启动容器
- 进入Nginx容器
- 删除容器
1 | # 1.访问Docker Hub搜索镜像 |
命令别名
比如在前面的案例中,我们需要对docker ps显示的内容进行格式化(format),指令过长,每一次都要输入这么多, 非常麻烦
1 | # 修改/root/.bashrc文件 |
然后,执行命令使别名生效:
1 | source /root/.bashrc |
所以当我们设置了这个, 如果需要查看镜像的情况, 则可以使用dps格式化别名指令
数据卷
容器是隔离环境,容器内程序的文件、配置、运行时产生的容器都在容器内部,我们要读写容器内的文件非常不方便 (比如我们进入到容器中使用vi指令修改index.html会提示没有指令).
数据卷(volume)是一个虚拟目录,是容器内目录与宿主机目录之间映射的桥梁
以Nginx为例,众所周知
- html: 放静态资源
- conf: 放配置文件
如果我们要让Nginx代理我们的静态资源,最好是放到html
目录;如果我们要修改Nginx的配置,最好是找到conf
下的nginx.conf
文件
但是非常遗憾的一点是, 不能直接去修改它(原容器), 容器运行的Nginx所有的文件都在容器内部。所以我们必须利用数据卷将两个目录与宿主机目录关联,方便我们操作。如图:
上图解答:
我们创建了两个数据卷html和conf
其中html对应nginx容器中的/html, 通过数据卷指向了宿主机中的文件/html, _data是用来存放数据自动创建的, 这样一来容器和宿主机就被
这样我们就可以在宿主机中的 /var/lib/docker/volumes/html/_data 存放资源, 那么容器中的数据也会被容器相对应的目录所映射
当前操作为 双向绑定, 宿主机自动映射到容器
/var/lib/docker/volumes目录为默认存放所有容器数据卷的目录, 在下根据数据卷的名称创建新目录, 例如: /数据卷名称/_data
数据卷命令
命令 | 说明 |
---|---|
docker volume create | 创建数据卷 |
docker volume ls | 查看所有数据卷 |
docker volume rm | 删除指定数据卷 |
docker volume inspect | 查看某个数据卷的详情 |
docker volume prune | 清除数据卷 |
Docker文档Docker Docs
注意:容器与数据卷的挂载要在创建容器时配置,对于创建好的容器,是不能设置数据卷的。而且创建容器的过程中,数据卷会自动创建。
案例
需求;
- 创建Nginx容器, 修改nginx容器内的html目录下的index.html文件内容
- 将静态资源部署到nginx的html目录
提示: 在执行docker run 命令时, 使用 -v 数据卷:容器内目录 可以完成数据卷挂载
当创建容器时, 如果挂载了数据卷且数据卷不存在, 则会自动创建数据卷
1 | # 1.首先创建容器并指定数据卷,注意通过 -v 参数来指定数据卷 |
如果不指定挂载目录,自动会为你挂载到/usr/lib下
挂载本地目录或文件
数据卷的目录结构较深,如果我们去操作数据卷目录会不太方便。在很多情况下,我们会直接将容器目录与宿主机指定目录挂载。挂载语法与数据卷类似:
1 | # 挂载本地目录 |
1 | -v mysql:/var/lib/mysql # 会被识别为一个数据卷叫mysql,运行时会自动创建这个数据卷 |
案例需求:
- 挂载
/root/mysql/data
到容器内的/var/lib/mysql
目录 - 挂载
/root/mysql/init
到容器内的/docker-entrypoint-initdb.d
目录(初始化的SQL脚本目录 —> 只有第一次才生效) - 挂载
/root/mysql/conf
到容器内的/etc/mysql/conf.d
目录(这个是MySQL配置文件目录)
解释: init为SQL脚本初始化, conf为配置文件, 其中配置文件主要是配置一些MySQL默认编码, utf8mb4
我们在虚拟机创建对应的目录,data, init, conf
本地目录挂载:(关于MySQL的挂载详情直达MySQL Image | Docker Hub)
1 | # 1.删除原来的MySQL容器 |
镜像
在这之前, 前面都是pull别人的镜像, 如果我也需要部署一个自己的镜像, 那么该怎么做呢?
镜像结构
在构建镜像之前, 我们需要先了解镜像的结果.
镜像之所以可以让我们快速跨操作系统部署应用而忽略其运行环境、配置,就是因为镜像中包含了程序运行需要的系统函数库、环境、配置、依赖。
所以不容易看出, 自定义镜像的本质就是依次准备好程序运行的基础环境、依赖、应用本身、运行配置等文件,并且打包而成
镜像是由层级结构组成: (在我们pull镜像的时候就不难看出)
Dockerfile
Dockerfile就是一个文本文件, 其中包含了一个个的指令(Instruction), 用指令来说明要执行什么操作来构建镜像。将来Docker可以根据Dockerfile来帮我们构建镜像。常见指令如下:
指令 | 说明 | 示例 |
---|---|---|
FROM | 指定基础镜像 | from centos:6 |
ENV | 设置环境变量,可在后面指令使用 | enu alue |
COPY | 拷贝本地文件到镜像的指定目录 | copy ./xx.jar /tmp/app.jar |
RUN | 执行Linux的shell命令,一般是安装过程的命令 | run yum install gcc |
EXPOSE | 指定容器运行时监听的端口,是给镜像使用者看的 | expose 8080 |
ENTRYPOINT | 镜像中应用的启动命令,容器运行时调用 | entrypoint java -jar xx.jar |
更多详细语法, 请参考官方文档 Dockerfile reference | Docker Docs
例如需要基于Ubuntu构建一个Java应用, 其中Dockerfile内容如下
1 | # 指定基础镜像 |
对于JDK镜像配置, 我们可以使用别人提供的JDK基础镜像, 简化Dockerfile
1 | # 基础镜像 |
最后相对于上面那个, 下面这个简洁了很多了
构建镜像
当Dockerfile编写好之后, 就可以使用命令来构建镜像了, 例如在当前目录下准备好了一个Dockerfile
文件和ClickCount.jar
项目jar包
1 | # 进入存放文件的目录 |
命令说明:
docker build
: 构建一个docker镜像-t
:是给镜像起名, 格式一人撒repository:tag的格式, 如果不指定tag,则默认为latest.
:最后的点代表当前目录(Dockerfile所在目录), 也可以指定其他目录的Dockerfile
1 | # 例如指定/root/dockerfile_demo目录 |
查看镜像列表
1 | # 查看镜像列表: |
网络
在上面的案例, 我们已经创建了Java项目的容器, 其中往往Java项目需要访问其他容器中间件(MySQL,Redis等), 他们需要访问则需要通过网络
默认情况下, 所有容器都是以bridge(网桥)方式连接到Docker的一个虚拟网桥上
1 | # 1.用基本命令,寻找Networks.bridge.IPAddress属性 |
发现Ping通, 并没有什么问题
但是,容器的网络IP其实是一个虚拟的IP,其值并不固定与某一个容器绑定,如果我们在开发时写死某个IP,而在部署时很可能MySQL容器的IP会发生变化,连接会失败
官方文档docker network | Docker Docs
常见网络命令有:
命令 | 说明 | 文档地址 |
---|---|---|
docker network create | 创建一个网络 | docker network create |
docker network ls | 查看所有网络 | docs.docker.com |
docker network rm | 删除指定网络 | docs.docker.com |
docker network prune | 清除未使用的网络 | docs.docker.com |
docker network connect | 使指定容器连接加入某网络 | docs.docker.com |
docker network disconnect | 使指定容器连接离开某网络 | docker network disconnect |
docker network inspect | 查看网络详细信息 | docker network inspect |
自定义网络
1 | # 1.首先通过命令创建一个网络 |
现在可以通过别名(例如: db)访问
总结:
- 在自定义网络中,可以给容器起多个别名,默认的别名是容器名本身
- 在同一个自定义网络中的容器,可以通过别名互相访问
(旧)项目部署示例
尝试部署黑马商城
项目结构:
- hmall: 商城的后端代码
- hmall-portal:商城用户端的前端代码
- hmall-admin:商城管理端的前端代码
部署的容器及端口说明:
项目 | 容器名 | 端口 | 备注 |
---|---|---|---|
hmall | hmall | 8080 | 黑马商城后端API入口 |
hmall-portal | nginx | 18080 | 黑马商城用户端入口 |
hmall-admin | nginx | 18081 | 黑马商城管理端入口 |
mysql | mysql | 3306 | 数据库 |
在正式部署前,我们先删除之前的nginx、dd两个容器:
1 | docker rm -f nginx dd |
mysql容器中已经准备好了商城的数据,所以就不再删除了。
部署SpringBoot项目
hamll项目是一个maven聚合项目, 打开项目结构如下
hamll
—–>hm-common 通用工具模块
—–>hm-service 业务模块
其中我们需要部署的是hm-service模块, 其中配置文件采用了多环境的方式
- application.yml
- application-dev.yml
- application-local.yml
其中的
application-dev.yaml
是部署到开发环境的配置,application-local.yaml
是本地运行时的配置
查看application.yaml,会发现其中的JDBC地址并未写死,而是读取变量:
1 | # 启动端口 |
这两个变量在application-dev.yml
和application-local.yml
中并不相同:
1 | # application-dev.yml |
在dev开发环境(也就是Docker部署时)采用了mysql作为地址,刚好是我们的mysql容器名,只要两者在一个网络,就一定能互相访问
将hm-service
目录下的Dockerfile
和hm-service/target
目录下的hm-service.jar
一起上传到虚拟机的root
目录
部署项目
1 | # 1.构建项目镜像,不指定tag,则默认为latest |
测试,通过浏览器访问:http://你的虚拟机地址:8080/search/list。
部署前端
hmall-portal
和hmall-admin
是前端代码,需要基于nginx部署。在黑马资料中已经提供了nginx的部署目录
其中:
html
是静态资源目录,我们需要把hmall-portal
以及hmall-admin
都复制进去nginx.conf
是nginx的配置文件,主要是完成对html
下的两个静态资源目录做代理
我们现在要做的就是把整个nginx目录上传到虚拟机的/root
目录下:
然后创建nginx容器并完成两个挂载:
- 把
/root/nginx/nginx.conf
挂载到/etc/nginx/nginx.conf
- 把
/root/nginx/html
挂载到/usr/share/nginx/html
由于需要让nginx同时代理hmall-portal和hmall-admin两套前端资源,因此我们需要暴露两个端口:
- 18080:对应hmall-portal
- 18081:对应hmall-admin
命令如下:
1 | docker run -d \ |
测试,通过浏览器访问:http://你的虚拟机ip:18080
(新)项目部署
在Windows和Linux下部署其实是一样的,首先我们需要将Dockerfile
和project.jar
放在一个目录下面
例如Dockerfile
示例如下
1 | # 添加 Java 8 镜像来源 |
然后jar包也在当前目录,例如 chat-server-1.0-SNAPSHOT.jar
然后我们需要输入指令
1 | docker build -f Dockerfile -t calyee/calyeechat:1.0 . |
然后就可以看到有镜像(calyee/calyeechat:1.0)了
我们需要将镜像跑起来
1 | docker run -d -p 8080:8080 -p 8090:8090 --name calyeechat calyee/calyeechat:1.0 |
在当前项目我们开了两个端口 所以需要映射两个端口出去(当前我们没有用到网桥,如需使用网桥可以参考整合下面小结#FastDFS)
然后 docker start calyeechat
即可,如果我们没有运行成功,需要查看报错信息,可以使用docker logs 容器id
.
DockerCompose
在当前的项目部署示例中, 我们部署了 1)MySQL容器 2)Nginx容器 3) Java项目, 但是在以后复杂的项目中, 当然还要部署其他的中间件, 从而远远不止类似于当前的三个容器, 如果还像之前那样部署, 非常麻烦
而Docker Compose就可以帮助我们实现多个相互关联的Docker容器的快速部署。它允许用户通过一个单独的 docker-compose.yml 模板文件(YAML 格式)来定义一组相关联的应用容器
基本语法
Docker Compose的YML语法详情见Compose file version 3 reference | Docker Docs
在docker-compose文件中可以定义多个相互关联的应用容器,每一个应用容器被称为一个服务
在之前的MySQL部署时, 我们使用的命令示例是这样的
1 | docker run -d \ |
而使用Compose, 是这样的
1 | version: "3.8" |
对比如下:
docker run 参数 | docker compose 指令 | 说明 |
---|---|---|
–name | container_name | 容器名称 |
-p | ports | 端口映射 |
-e | environment | 环境变量 |
-v | volumes | 数据卷配置 |
–network | networks | 网络 |
那么对于刚刚项目部署的样例则可以修改为
1 | version: "3.8" |
基本命令
编写好了Yaml文件, 就可以部署项目了
对于一些常见的命令 Overview of docker compose CLI | Docker Docs
语法如下
1 | 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 | 在指定的运行中容器中执行命令 |
样例: Docker整合FastDFS
镜像
1 | # 1. 查找FastDFS |
tracker安装
1 | docker run -d --name tracker --network=yournetwork -v E:\dockercontainers\fastdfs\tracker:/var/fdfs delron/fastdfs tracker |
E:\dockercontainers\fastdfs\tracker
为挂载到本地的路径--network=yournetwork
加入的网桥
storage安装
1 | docker run -d -p 8888:8888 --name storage --network=yournetwork -e TRACKER_SERVER=172.18.0.2:22122 -v E:\dockercontainers\fastdfs\storage:/var/fdfs -e GROUP_NAME=group1 delron/fastdfs storage |
TRACKER_SERVER
为上面的tracker
连接到的网桥的地址GROUP_NAME
分组名
测试: 在storage容器中测试使用命令上传图片
1 | # 进入容器内bash指令模式 |
应用: SpringBoot整合FastDFS
导入第三方依赖
1 | <!--fastDFS--> |
配置SpringBoot Yaml
1 | fdfs: |
编写一个简单的UploadService
1 | // 注入Fast客户端 |
该starter源码参考: [luhuiguo](luhuiguo/fastdfs-spring-boot-starter: Spring Boot FastDFS Starter (github.com)), 关于此依赖版本, 参考maven仓库, 搜索fastdfs (当前github中的readme文件介绍指定的是0.1.0)
应用通信
环境: SpringBoot项目,Docker容器有: Redis, MySQL, FastDFS
当前项目的拓扑图示例如下:
对于Windows访问容器: Windows访问
暴露的端口 , 例如访问docker容器中的nginx暴露的8888端口, 从而访问FastDFS存储的照片对于Docker容器内部通信: 容器之间可以通过网桥通信, 例如我的Application应用(SpringBoot), 需要访问MySQL和Redis等容器, 则可以访问网桥分配的ip地址(如图), 实现通信。查看如何查询网桥情况:
docker network inspect your_bridge
关于线上环境: 在Docker内部则是通过网桥访问容器原生端口(例如MySQL:3306), SpringBoot的配置文件配置样例
1 | fdfs: |
例如Redis在网桥上的IP为172.18.0.5, 则需要在配置文件中配置网桥地址。 (结构图参考该小结首照片)