一、Docker 安装、配置与卸载
1.1、Docker 安装
# 1.安装gcc环境
yum -y install gcc gcc-c++ && \
# 2. 卸载docker旧版本(可能之前有安装)
yum -y remove docker docker-common docker-selinux docker-engine && \
# 3. 安装依赖的软件包
yum install -y yum-utils device-mapper-persistent-data lvm2 && \
# 4. 设置stable仓库
yum-config-manager --add-repo http://mirrors.aliyun.com/docker-ce/linux/centos/docker-ce.repo && \
# 5. 更新yum软件包索引
yum makecache fast && \
# 6. 安装Docker社区版
yum -y install docker-ce && \
# 7. 启动Docker
systemctl start docker && \
# 8. 测试是否安装成功
docker version
1.2、Docker 卸载
# 1. 卸载docker之前需要先停止
systemctl stop docker && \
# 2. 卸载docker
yum -y remove docker-ce && \
# 3. 清除残留文件
rm -rf /var/lib/docker
1.3、配置 Docker 仓库的国内镜像加速
VAR_PATH_ETC_DOCKER_DIR="/etc/docker" && \
VAR_PATH_ETC_DOCKER_DAEMON_JSON="$VAR_PATH_ETC_DOCKER_DIR/daemon.json" && \
# 1. 创建存放配置文件的文件夹
mkdir -p $VAR_PATH_ETC_DOCKER_DIR && \
# 2. 创建配置文件
# 配置文件的具体内容因人而异,需要登录[阿里云开发者平台](https://dev.aliyun.com/search.html)进行查看
cat << EOF > $VAR_PATH_ETC_DOCKER_DAEMON_JSON
{
"registry-mirrors": ["xxx"]
}
EOF
# 重新加载配置文件
systemctl daemon-reload && \
# 重启docker
systemctl restart docker
二、Docker 命令
2.1、命令分类
子命令分类 | 子命令 |
---|---|
Docker 环境信息 | info、version |
容器生命周期管理 | Create、exec、kill、pause、.restart、rm、run、start、stop、unpause |
镜像仓库命令 | login、logout、pull、push、search |
镜像管理 | build、images、import、load、rmi、save、tag、commit |
容器运维操作 | attach、export、inspect、port、ps、rename、stats、top、wait、cp、diff、update |
容器资源管理 | volume、network |
系统日志信息 | events、history、logs |
2.2、Docker 进程命令
命令 | 作用 |
---|---|
systemctl start docker | 启动 docker 服务 |
systemctl stop docker | 停止 docker 服务 |
systemctl status docker | 查看 docker 服务状态 |
systemctl restart docker | 重启 docker 服务 |
systemctl enable docker | 设置 docker 开机自启动 |
2.3、Docker 容器命令
容器命令 | 作用 |
---|---|
docker run ... | 基于镜像创建容器并启动(第一次使用,不能重复创建同名容器) |
docker rm <容器名> | 删除容器 |
docker start <容器名或容器id> | 启动容器 |
docker stop <容器名或容器id> | 停止容器 |
docker kill <容器名或容器id> | 杀死容器 |
docker ps | 查看当前运行中的容器 |
docker ps -a | 查看所有容器 |
docker exec -it <容器名> /bin/bash | 进入到容器中 |
docker stop <容器id> | 停止容器 |
docker inspect <容器名> | 查看容器信息(IP 地址等) |
docker port <容器名> | 查看容器的端口映射信息 |
docker attach <容器名> | 用途未知 |
docker commit <容器名> | 将正在运行的容器制作成一个镜像 |
2.4、Docker 镜像命令
镜像命令 | 作用 |
---|---|
docker images | 列出本地机器中的镜像 |
docker search <镜像名> | 在 docker hub 中进行查找镜像 |
docker pull <镜像名>:<版本号> | 下载镜像,没指定版本号时默认为最新版 |
docker rmi -f <镜像名>:<版本号> | 强制删除镜像 |
三、Docker 容器数据卷
可以在 IDEA 中通过 service 来调用出下面的面板
3.1、数据卷
数据卷出现的目的是为了解决以下的两个问题:
- Docker容器删除后,在容器中产生的数据也会随之消失
- Docker容器和外部机器,Docker容器和Docker容器之间不可以直接交换文件
如果想要进行多个docker容器之间的数据交换,可以将多个容器挂载到同一个数据卷(共享文件夹)
3.2、目录挂载
数据卷在宿主机(Linux)上,挂载到 Docker 容器中。类似文件在U盘上,然后U盘接入到电脑中(挂载)。电脑重装系统并不会删除 U 盘中的数据,类似删除 Docker 容器并不会删除数据卷中的数据。
文件挂载通过 -v
指定参数 docker run -v <宿主机目录>:<docker容器内目录> --privileged=true
--privileged=true
表示 docker
额外的功能:
- 限制容器内的目录只能读取,在上面的命令中添加
:ro
(read only),构成-v <宿主机目录>:<容器内目录>:ro
3.3、容器之间的数据卷继承
期望不同容器之间的挂载目录保持相同,可以使用数据卷继承. 需要注意的是,继承的是文件挂载的规则
--volumes-from <希望保持相同挂载规则的容器名>
数据卷的继承可以在不知道其它容器具体的挂载规则的情况下,复用该规则。继承的挂载规则是独立的,子容器的挂载规则不会因为父容器的删除而消失。
四、Dockfile
镜像原理
镜像是一层层文件堆叠而成,镜像是只读的,而容器是镜像上面在堆叠一层可写的文件,对于镜像生成的原始容器的操作都记录在容器层对应的文件中?
制作镜像
commit 制作镜像
对运行中的容器进行修改,然后通过 docker commit <容器名> <指定镜像名>:<指定版本号>
也可以创建镜像。这种方式的优点是简单,缺点在于不是自动化的,不适合复现。例如,在写博客教程时,读者需要按照博主相同的操作流程对 docker 容器进行修改配置,不能通过一行代码直接得到相同的环境。所以后面主要介绍通过 Dockerfile 来创建镜像。
通过 commit 方式制作的镜像不会打包挂载的数据卷吗?如果数据卷是容器和宿主机独立的两份,为什么这里又不会打包挂载的数据卷呢?数据卷是容器和宿主机共享的,正因为数据卷是宿主机的文件夹挂载在容器中(搞清楚谁挂载在谁上很重要,类比 U 盘插入电脑,数据卷挂载在容器中),数据卷不属于docker的文件系统,因此容器被打包成镜像时不会打包挂载的数据卷。
Dockerfile 制作镜像
Dockerfile 文件中的每一行构建一层镜像,这就是RUN尽可能一个模块写在一起并使用 && 进行连接的原因?
# FROM: 指定基础镜像
FROM centos:7
# MAINTAINER: 指定作者信息
MAINTAINER xiong
# ENV: 设置环境变量
ENV USERNAME root
ENV PASSWORD root
# RUN: 执行shell命令
RUN yum install -y vim
RUN apt-get update && apt-get install -y \
python
python-pip
RUN pip install numpy
# ADD: 将宿主机文件复制到容器中
ADD hello.py /tmp/hello.py
# WORKDIR: 指定工作目录
WORKDIR /
# EXPOSE: 暴露端口(谁的端口?)
EXPOSE 5000
# 接收docker run中的输入的字符串作为echo命令的参数,一般用于制作一些执行后关闭的容器
ENTRYPOINT ["/bin/echo"]
# CMD: 容器启动时执行该命令,后面是提供echo命令的参数
CMD ["/bin/echo","Hello,Dockerfile!"]
定义Dockerfile,发布SpringBoot项目(假设为springboot-hello.jar)
-
创建一个 Dockerfile 文件,文件名和后缀都任意,假设为 springboot_hello.dockerfile。Dockerfile 文件和 jar 包放到同一个目录下。
# jar包执行需要JRE环境 FROM java:8 ADD springboot-hello.jar ./spring-hello.jar CMD java -jar spring-hello.jar
-
输入
docker build -f <Dockerfile文件路径> -t <镜像名>:<版本号>
命令来构建镜像docker build -f springboot_hello.dockerfile -t myHello:1.0
保存镜像
容器和镜像不能直接传输,需要将其保存成压缩文件,才能实现镜像的共享。(为什么不直接发布到 docker hub 或私有仓库上,让别人去拉取呢?)
- 保存镜像:
docker save -o <tar文件名> <镜像名>:<版本号>
- 加载镜像:
docker load -i <tar文件名>
- 保存容器:
docker export
- 加载容器:
docker import
保存镜像(先commit,再save)会保留它的历史,保存容器会对它的历史进行压缩,二者类似。
# 将之前创建的redis-demo镜像保存为tar文件.
docker save -o redis-docker.tar redis-demo:1.0
# 输出路径为当前目录
五、Docker 网络
每个容器中只运行一个进程。容器和容器之间通过容器链接或者其它的容器网络技术来进行通信
获取容器的 IP 地址:docker inspect <容器名> | grep "IPAddress"
如果在创建容器时不使用端口映射,相当于在宿主机和容器之间搭建了一个私人网络,在宿主机上可以通过查找容器 IP + 容器端口号来访问容器,但是在其它机器上无法访问容器。
5.1、容器链接(过时)
容器链接 docker run --link
在单台主机上可以正常工作,但是在一个大规模系统中,需要使用其它的服务发现方式。可以使用键值存储和 DNS 作为解决方案。另外,Docker Network 提供了一种内建机制来将容器内的服务暴露给外部,而不必使用容器链接。
5.2、用户定义网络
默认情况下使用 bridge
网络,但该网络的问题是不能将容器名解析为 IP 地址,例如不可以通过 ping <容器名>
的方式来进行容器间的通信。而用户定义的网络则解决了这个问题。
创建网络:docker network create <网络名>
连接网络:
- 创建容器时连接到指定网络
docker run --name --network <指定连接的网络名>
- 运行中容器连接到指定网络
docker network connect <指定连接的网络名> <容器名>
断开网络连接:docker network disconnect <网络名> <容器名>
查看网络信息:docker network inspect <网络名>
从用法上来看,网络和容器的使用方法类似,只是使用 docker network
作为命令的前缀,例如 docker network ls
等等。
六、Docker Compose 服务编排
6.1、服务编排
微服务架构中一般包含多个微服务,每个微服务一般都会部署多个实例,如果每个微服务都要手动启动和停止,维护的工作量会非常大。而服务编排就是按照一定的业务规则批量管理容器,而 Docker Compose 就是进行服务编排的工具之一。Docker Compose 的使用步骤:
- 编写 Dockerfile 文件
- 使用 docker-compose.yml 文件定义组成应用程序的各个微服务
- 运行 docker-compose up 来启动应用程序
6.2、Docker Compose 的使用
docker compose 默认被安装,可以通过 docker compose version
查看
使用 docker compose 来编排 nginx + springboot 项目,其中 nginx 能够反向代理多个 springboot 项目生成的容器。
-
编写 docker-compose.yml 文件
docker-compose 就是将 docker run 命令中的各种参数配置使用 yaml 的方式进行重新编排
version: '3' services: nginx:冲冲冲 image: nginx ports: # 短线"-"在yaml文件中的含义表示是数组 # 这里的80:80是没有空格的,因为代表的是一个属性值<host_port>:<docker_port> - 80:80 # 需要连接的其它容器,与其进行通信 links: - app1 - app2 # 数据卷挂载 volumes: # nginx的配置文件名叫啥都可以,以".conf"结尾即可,推荐为nginx.conf,在挂载目录中创建即可 - ./nginx/conf.d:/etc/nginx/conf.d # springboot-hello是springboot项目生成的一个docker镜像 # app1 是生成的容器 app1: image: springboot-hello expose: - "8080" app2: image: springboot-hello expose: - "8081"
-
编写 nginx 的配置文件
server { listen 80; access_log off; location / { # 配置反向代理 proxy_pass http://app1:8080 http://app2:8081; } }
-
使用
docker compose up
来执行 docker-compose.yml 文件中信息
分布式系统中的容器通信该如何处理,至今未正面面对这个问题。K8S?
七、通过 Docker 安装其他软件
7.1、Docker 安装 MySQL
-
拉取 MySQL 的镜像
docker pull mysql:latest
-
查看本地镜像
docker images
-
运行容器
docker run \ --name mysql-docker \ -p 3306:3306 \ -e MYSQL_ROOT_PASSWORD=root \ -v /opt/module/data/mysql/data:/var/lib/mysql \ -v /opt/module/data/mysql/log:/var/log/mysql \ -v /opt/module/data/mysql/lib:/var/lib/mysql-files \ -v /opt/module/data/mysql/conf:/etc/mysql \ -d mysql
参数介绍 --name mysql-docker
指定创建的容器名为 mysql-docker -p 3306:3306
前一个 3306 是 Linux 宿主机上的端口,后一个 3306 是 mysql-docker 容器中的端口 -e MYSQL_ROOT_PASSWORD=root
设置环境变量 MySQL 的 root 用户的密码为 root -v /opt/module/data/mysql/data:/var/lib/mysql
指定 MySQL 的数据挂载目录 -v /opt/module/data/mysql/log:/var/log/mysql
-v /opt/module/data/mysql/lib/mysql-files:/var/lib/mysql-files
-v /opt/module/data/mysql/conf:/etc/mysql
-d mysql:latest
指定使用 mysql:latest 这个镜像来启动容器 -p <宿主机的port>:<容器的port>
:实现容器端口到宿主机端口的映射。外部机器不可以与宿主机的容器直接通信,宿主机可以与容器直接通信,外部机可以与宿主机直接通信。通过端口映射,例如宿主机的3307对应着访问容器的3306,当外部机器需要访问容器的3306端口时,访问宿主机的3307端口即可 -
使用
docker ps
来查看正在运行中的容器(取名为 mysql-docker) -
在宿主机上修改 MySQL 的配置
vim /opt/module/data/mysql/conf/my.cnf
# 防止中文乱码 [client] default-character-set=utf8 [mysql] default-character-set=utf8 [mysqld] init_connect='SET collation_connection = utf8_unicode_ci' init_connect='SET NAMES utf8' character-set-server=utf8 collation-server=utf8_unicode_ci skip-character-set-client-handshake skip-name-resolve #################################### 集群配置 ################################### # mysql集群中的id,集群下需要唯一 server_id=101 # 指定不需要同步的数据库名称 binlog-ignore-db=mysql # 开启二进制日志功能 log-bin=mall-mysql-bin # 设置二进制日志使用内存大小(事务) binlog_cache_size=1M # 设置mysql集群使用的binlog日志的格式(mixed,statement,row) binlog_format=mixed expire_logs_days=7 slave_skip_errors=1062 ############################ MySQL从服务器需要额外配置下面参数 ########################## # relay_log 配置中继日志 relay_log=mall-mysql-relay-bin # log_slave_updates表示slave将复制事件写入自己的binlog日志中 log_slave_updates=1 # slave设置为只读 read_only=1
-
重启 mysql-docker 来应用更新后的配置
docker restart mysql-docker
-
使用
docker exec -it mysql-docker
进入到 mysql-docker 容器中,成功进入代表部署成功 -
使用
whereis mysql
命令来查看 MySQL 的安装位置 -
使用
cat /etc/mysql/my.cnf
来查看在宿主机上修改的配置文件 -
使用
mysql -uroot -proot
来进入 MySQL,在其中创建一个数据库,方便后面的测试连接 -
在 IDEA 中进行外部的连接测试
-
(主从复制)MySQL 主服务器中开启主从复制
这部分感觉可以不用进行配置?直接把root用户给SLAVE服务器?
CREATE USER 'slave'@'%' IDENTIFIED BY '123456'; GRANT REPLICATION SLAVE,REPLICATION CLIENT ON *.* TO 'slave'@'%';
-
(主从复制)在主服务器上获取一些用于主从复制的信息
SHOW MASTER STATUS;
-
(主从复制)在从服务器中指定主服务器
CHANGE MASTER TO MASTER_HOST='<mysql主服务器的ip地址>', MASTER_USER='root', MASTER_PASSWORD='root', MASTER_PORT=3306, MASTER_LOG_FILE='mall-mysql-bin.000001', MASTER_LOG_POS=617, MASTER_CONNECT_RETRY=30;
上面的信息需要根据 MySQL主服务器上情况进行修改
-
(主从复制)在 MySQL 从服务器中开启主从复制
START SLAVE;
-
(主从复制)主从复制配置成功的测试
7.2、Docker 安装 Redis
-
创建并启动 Redis
docker run \ -p 6379:6379 \ --name redis \ -v /opt/module/data/redis/data:/data \ -v /opt/module/data/redis/conf/:/etc/redis/conf \ -d redis \ redis-server /etc/redis/conf/redis.conf
最后一行的 redis-server /etc/redis/redis.conf 实际上是 redis 启动服务端的命令,难道说 docker run 可以直接执行 sh 命令吗?那是否可以写上 mysql -uroot -proot 来直接登录 MySQL 呢?
redis-server /etc/redis/conf/redis.conf 是指定redis-docker容器启动时使用的配置文件,由于上面没有创建 redis.conf 文件,所以实际上并没有成功启动。可以通过
docker logs -f redis-docker
查看日志,能够看到 Fatal error,can’t open config file ‘/etc/redis/conf/redis.conf’: No such file or directory 这样的错误。 -
在宿主机的挂载目录
/opt/module/data/redis/conf
中创建 redis.conf 文件,并设置允许远程访问 Redis挂载目录实际上就是容器内的目录,相当于创建了一个快捷方式(软链接),对挂载目录中的操作实际上是对容器内部的目录进行操作。
bind 0.0.0.0 protected-mode no
-
使用
docker start redis
来真正启动Redis容器 -
使用 redis-docker 容器来执行连接 Redis
docker exec -it redis redis-cli
这里标明不需要进入到容器中再去执行 redis-cli 命令,做了一点点简化。
7.3、Docker 安装 Redis 集群
配置一个 3 主 3 从的 Redis 集群
-
创建一个用户自定义网络,不妨命名为
redis-network
docker network create redis-network
-
下面的配置文件复制 6 份,启动 6 个 Redis 容器
(redis-master-6380、redis-master-6381、redis-master-6382、redis-slave-6383、redis-slave-6384、redis-slave-6385)docker run \ -p 6380:6379 \ --name redis-master-6380 \ --network redis-network --privileged=true \ -v /opt/module/data/redis/data:/data \ -v /opt/module/data/redis/conf/:/etc/redis/conf \ --cluster-enabled yes \ --appendonly yes \ -d redis \ redis-server /etc/redis/conf/redis.conf
-
将多个 Redis 节点合并成一个 Redis 集群(不指定 master 和 slave)
redis-cli --cluster create \ <ip1>:<port1> \ <ip2>:<port2> \ <ip3>:<port3> \ <ip4>:<port4> \ <ip5>:<port5> \ <ip6>:<port6> \ --cluster-replicas 1 \ --cluster-yes
-
为 master 节点添加 slave 节点
其中
<node-id>
通过redis-cli cluster nodes
来获取redis-cli \ -h <ip1> -p <port1> \ cluster replicate <node-id>
7.4、Docker 安装 Nginx
-
创建并启动 Nginx
docker run \ -p 8000:80 \ --name nginx-docker \ -v /opt/module/data/nginx/conf:/etc/nginx/conf \ -v /opt/module/data/nginx/log:/var/log/nginx \ -d nginx
八、安装过程中的错误记录
使用 docker ps 查看正在运行的容器
使用 docker logs mysql-docker
来查看容器报错日志
可能存在的问题:
- 如果这里并没有 mysql 容器在运行,那么有可能是因为端口冲突,或者一些其它别的问题?(换一台主机测试成功)
- 可能一开始能够通过
docker ps
查看到运行中的 mysql-docker,但是隔一段时间后发现 mysql-docker 停止了,这也说明 mysql-docker 并没有成功运行。