一、在本机上安装并设置 Docker
首先到 Docker 官网 (https://docs.docker.com/) 下载并安装对应你操作系统的最新 Docker 桌面版。我的操作系统是 macOS,对应的版本是 Docker Desktop for Mac。
Windows 用户请确保你的操作系统是 Windows 10 64 位专业版。如果你的 Windows 操作系统不是 Windows 10 64 位专业版,建议安装虚拟机,在虚拟机里面安装 Ubuntu 服务器,然后在 Ubuntu 中安装 Docker。不建议安装低版本的 Docker Toolbox for Windows。
Windows 10 build 16215 及以后版本就不需要安装虚拟机了,建议使用内置的 WSL(Windows Subsystem for Linux),在 Ubuntu 中安装 Docker。
等待 Docker 启动完成后,打开命令行终端,在终端下输入如下图所示 docker -v 命令,并回车。
如果出现 Docker 版本的提示,那么恭喜你,Docker 安装成功了!
我安装的是稳定版的 2.2.0.5,如下图。
下一步,我们设置 Docker 国内镜像加速,否则拉镜像到本机的时候,会慢到你忍无可忍,相信我 -:)
桌面版因为带有GUI界面,设置从国内镜像加速其实特别简单。右键单击 Docker 图标,选中设置 Setting(Windows下)或者 Preferences(macOS下),就会弹出如下的 Docker 对话框。点击 Docker Engine,把下面代码里的国内镜像加速地址贴到Docker守护线程 daemon 的配置文件中。
{ "debug": true, "experimental": false, "registry-mirrors": [ "http://hub-mirror.c.163.com" ]}
我用的是网易的镜像加速 http://hub-mirror.c.163.com。当然可以多配置几个镜像,防止镜像服务可能出现宕机,比如再加一个腾讯云的镜像 https://mirror.ccs.tencentyun.com:
{ "debug": true, "experimental": false, "registry-mirrors": [ "http://hub-mirror.c.163.com", "https://mirror.ccs.tencentyun.com" ]}
完了请点击 Apply & Restart 按钮,重启 Docker 守护进程。然后转到命令行终端,输入如下命令,查看 Docker 的信息:
docker info
如果看到在输出的信息中如下字样,就说明镜像配置成功了。
Registry Mirrors: http://hub-mirror.c.163.com/
OK,Docker 的安装算是搞定了。
下一小节,我们就开始在 Docker 容器中玩 MySQL。
二、在容器中玩 MySQL
详细文档,请参考 MySQL 官网中[使用 Docker 在 Linux 上部署 MySQL](https://dev.mysql.com/doc/refman/5.7/en/linux-installation-docker.html)一文。
1. 小试牛刀
打开命令行终端,输入如下命令,搜一下 Docker 仓库中的 mysql 镜像:
docker search mysql
运行结果如下图所示:
我们就选 Stars 最多的官方版本(Offical 列的值为 OK)好了。输入如下命令,把官方 5.7 版本的 mysql 拉到本机上:
docker pull mysql:5.7
等镜像下拉完成后,在命令行终端上继续输入如下 docker 命令,查看已经下拉到本机上的镜像:
docker images
好了,MySQL5.7 的 Docker 镜像已经拉到你的本机上了。
接下来,我们就开始在容器中体验 MySQL。
不管三七二十一,我们先把容器启动起来。启动一个 MySQL 容器很简单,只需要执行如下命令即可:
docker run --name some-mysql -e MYSQL_ROOT_PASSWORD=my-secret-pw -d mysql:tag
这里,`some-mysql` 是你想给容器取的名字,`my-secret-pw` 是你给 MySQL root 用户设置的密码,`tag` 是我们指定的 MySQL 版本。
在本机命令行终端输入如下命令:
docker run -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
上面命令的意思是从镜像 mysql5.7 以后台运行方式(`-d mysql:5.7`)运行容器,容器的名称为 mysql(`--name mysql`),并将本机的 3306 端口映射到容器的 3306 端口(`-p 3306:3306`),同时向容器传入一个环境变量,设置 MySQL Root 帐号密码为 123456(`-e MYSQL_ROOT_PASSWORD=123456`)。
现在,我们的 MySQL5.7 容器就在后台运行起来了。如果右键点击 Docker 运行图标,选中仪表板(Dashboard),就会弹出如下窗口:
在仪表盘上,我们可以直接重启容器、停止容器、删除容器。如果点击正在运行的 mysql 容器,可以看到容器的各种信息,如下图所示。
执行 docker inspect 命令,可以看到这个容器更详细的信息:
docker inspect mysql
现在我们就可以通过任意一个 MySQL 客户端(比如,MySQL 官方的免费客户端 MySQLWorkbench、Intellij IDEA IDE 中的数据库工具等等)连接到这个容器,执行数据库操作了。不过更常用的方式是在命令行终端中通过 docker exec -it 命令进入一个正在运行的容器,以命令行交互式方式执行操作。OK,下面我们就来试试。
在命令行终端中输入如下命令,进入正在运行的容器 `mysql`:
docker exec -it mysql /bin/bash
进入以后,就来到了容器中的命令行提示符下。注意哦,这里是容器中的命令行提示符下,不是本机的。
在容器的命令行提示符下输入:
mysql -u root -p
输入我们设置的 root 用户密码 123456,并回车,就进入 MySQL 操作提示符下了,如下图所示。
在 MySQL 提示符下,依次输入以下命令,看看会出现什么:
show databases;use mysql;create database test;use test;create table student(studentid int auto_increment primary key, studentname varchar(20));show tables;insert into student(studentname) value('xiao');insert into student(studentname) value('hu');select * from student;
OK,现在我们已经在 MySQL 容器中创建了一个示例数据库 `test`,并在 `test` 中创建了一个表 `student`,然后插入了两条测试记录。
现在我们退出容器,返回到本机命令行终端。
首先输入 exit; 命令,退出 MySQL 命令行提示符,返回到容器的命令行终端。然后继续输入 exit 命令,退出容器,返回到本机命令行终端。
现在输入如下命令,显示本机 Docker 中正在运行的容器:
docker psCONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMESd7ae184971dc mysql:5.7 "docker-entrypoint.s…" 2 hours ago Up 2 hours 0.0.0.0:3306->3306/tcp, 33060/tcp mysql
我们创建的 MySQL 容器还在运行中,输入如下命令,把它停掉:
docker stop mysql
再执行 docker ps 命令,就会可以看到 MySQL 容器已经不在了。
如果要看以前执行过哪些容器,那就输入 docker ps -a 命令。
输入 docker restart 后跟容器ID或者容器名,就可以重新启动已经退出的容器。比如,输入如下命令,就可以把我们刚停掉的 MySQL 容器重新启动起来。
docker restart mysql
OK,学会这一招,以后咱们就不用在本机上安装 MySQL 了。需要用的时候就启动容器,不需要的时候关掉。不用再把 MySQL 安装为本机系统服务,占据开发机宝贵的内存资源 -:)
现在我们把 MySQL 容器停掉,然后把这个容器删除掉:
docker stop mysqldocker rm mysql
再执行 docker ps -a,你会发现 mysql 容器彻底消失了。
啊!等一等,我的数据。。。。
2. 数据卷
容器一删除或者损坏,我们在容器中创建的数据库、表、配置以及数据也全都没了。怎么办呢?Docker 提供了数据卷的机制来持久化在 Docker 容器中创建的数据。通过数据卷机制,我们可以把 MySQL 的数据文件、日志文件放在本机上,防止手残删掉容器导致数据丢失。另外,我们也可以把 MySQL 配置文件放在数据卷中,这样我们需要调整 MySQL 服务器配置的时候,就不用进入容器,直接在本机上修改即可,这样子会方便很多。
在执行 Docker run 命令时加上参数 `-v 本机目录:容器目录`,就可以将本机一个目录挂载到容器中某个目录,让本机目录变成 Docker 容器的数据卷。我们要把 MySQL 的数据文件、日志文件、配置文件放在本机,就必须知道该把本地目录挂载到容器的哪个目录。MySQL 容器有自己默认的数据文件、日志文件安装目录,但是最方便的是我们自己在 MySQL 配置文件中设置。
我们先来看看 MySQL 容器中 MySQL 服务器的配置文件 My.cnf 在哪里。为此,我们再执行一次以下命令,重新启动一个 MySQL 容器:
docker run -p 3306:3306 --name mysql -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
输入如下命令,查看容器中 MySQL 服务器的安装目录:
which mysqld/usr/sbin/mysqld
得到的 MySQL 服务器安装目录为 `/usr/sbin`。
通过如下命令查看 MySQL 使用的配置文件默认路径:
/usr/sbin/mysqld --verbose --help |grep -A 1 'Default options'/etc/my.cnf /etc/mysql/my.cnf ~/.my.cnf
服务器首先读取的是 `/etc/my.cnf`,如果前一个文件不存在就继续读 `/etc/mysql/my.cnf` 文件,如果还不存在就会去读 `~/.my.cnf` 文件。
OK,我们记住 `/etc/my.cnf`,这个首选的配置文件路径。
然后输入如下命令,输入密码 `123456`,进入到 MySQL 客户端命令行:
mysql -u root -p
然后输入如下命令,显示系统变量中带有 'dir' 的系统变量:
show variables like '%dir%';
在我的电脑上,输出如下图所示:
我们记住 MySQL 数据文件目录的 `datadir` 的值 `/var/lib/mysql`,MySQL 临时文件目录 `tmpdir` 的值 `/tmp`。
输入 `exit()` 退出 MySQL 客户端命令行,输入 `exit` 退出容器,返回本机命令行终端,然后输入如下命令,停掉容器,然后删除容器:
docker stop mysqldocker rm mysql
在本机命令行终端上输入如下命令,先新建一个目录 `mysql`,然后进入该目录,新建一个子目录 `data`,用来存放 MySQL 数据文件;新建一个子目录 tmp,用来存放 MySQL 临时文件;新建一个文件 `my.cnf`,用来写入 MySQL 配置文件:
mkdir mysqlcd mysqlmkdir datamkdir tmptouch my.cnf
在本机上用文本编辑器打开 `my.cnf`,输入如下内容:
[mysqld]user=mysqldatadir=/var/lib/mysqltmpdir=/tmpcharacter-set-server=utf8mb4collation_server=utf8mb4_unicode_cidefault_authentication_plugin=mysql_native_password[client]default-character-set=utf8mb4[mysql]default-character-set=utf8mb4
存盘退出。在本机命令行终端上输入如下命令,启动 MySQL 容器,同时将数据卷挂载到容器:
docker run -p 3306:3306 --name mysql -v /Users/shaw/mysql/my.cnf:/etc/my.cnf -v /Users/shaw/mysql/data:/var/lib/mysql -v /Users/shaw/mysql/tmp:/tmp -e MYSQL_ROOT_PASSWORD=123456 -d mysql:5.7
解释一下:
- `-v /Users/shaw/mysql/my.cnf:/etc/my.cnf` 是将本机的 `/Users/shaw/mysql/my.cnf` 文件映射成容器内的 `/etc/my.cnf` 文件。
- `-v /Users/shaw/mysql/data:/var/lib/mysql` 是将本机的 `/Users/shaw/mysql/data` 挂载成容器内的 `/var/lib/mysql` 目录。
- `-v /Users/shaw/mysql/tmp:/tmp` 是将本机的 `-v /Users/shaw/mysql/tmp` 目录挂载成容器内的 `/tmp` 目录。
而我们在 `my.cnf` 文件内,将数据目录 `datadir` 设置为 `/var/lib/mysql`,将临时文件目录 `tmpdir` 设置为 `/tmp`,因此,MySQL 容器启动后,就会将 MySQL 数据库的数据文件和临时文件放在本机对应的目录下。
如果我们进入容器,在 MySQL 命令行终端中重新输入前面创建数据库的 SQL 语句:
use mysql;create database test;use test;create table student(studentid int auto_increment primary key, studentname varchar(20));show tables;insert into student(studentname) value('xiao');insert into student(studentname) value('hu');select * from student;
然后退出容器,转到本机上查看我们前面创建的存放数据卷的目录,会看到 MySQL 已经所有生成的数据文件和临时文件放到这个目录下,如下图所示。
注意事项:
- 如果在执行 `docker run` 命令后,执行 `docker ps` 看不到 MySQL 容器,说明 MySQL 启动不成功,我们可以输入 `docker logs 容器名` 查看哪里出错了。
- 如果我们在本机创建的数据卷目录没有包含在 Docker 默认的文件共享目录中,执行 docker run 挂载数据卷时就会出现只读文件系统错误。解决方法是将目录加入到文件共享中,如下图所示。
个人学习 Docker 时的一点体会:虽说 Docker 学习起来很简单,但是 Docker 命令以及命令参数还真是不少,如果玩玩一会儿就不用它,很容易忘记。必须坚持多在命令行终端上多玩一段时间,多出错,才能熟记于心。
3. 使用 Docker 时的两个坑
1)容器与主机系统时间不同步的问题
在本机命令行终端上执行如下两条命令,你会发现容器里面的时间比主机时间晚 8 个小时:
datedocker exec 容器名 date
主要原因是:容器与主机采用了不同的时区。比如,在我的机器上:
容器里面用的是 UTC(世界标准时间或世界协调时间,Coordinated Universal Time),而我的电脑上用的是 CST(中国标准时间,China Standard Time)。在 Linux 系统中,系统时间是由时间和时区决定的。容器里面,文件 `/etc/localtime` 是用来描述容器的时间,而文件 `/etc/timezone` 是用来描述容器所属的时区。要想让本机时间时区和容器时间时区保持一致,我们得修改容器中这两个文件的内容。
Linux 时区文件都放在 `/usr/share/zoneinfo` 目录下,在中国选 `/usr/share/zoneinfo/Asia/Shanghai` 这个文件,执行如下命令,把这个文件复制成 `etc/localtime`,并把时区文件 timezone 的内容设置为 `Asia/Shanghai` :
cp /usr/share/zoneinfo/Asia/Shanghai /etc/localtimeecho `Asia/Shanghai` > /etc/timezone
然后再执行 date 命令,就可以看到容器的时间时区就跟我们本机操作系统的时间一致了。
2)中文编码问题
如果往容器里面复制带中文的文件、目录等等,中文都会乱码。比如,我们在容器的 `/home` 目录下,创建一个文件名含有中文的文件 `中文.txt`,然后将其复制到名为 `mysql` 容器中的 `/home` 目录下,进入容器,查看 `/home` 目录,我们发现复制进去文件名乱码了,如下图所示。
这是因为当前容器中 Linux 操作系统的当前编码不支持中文,所以会乱码。在容器的命令行终端下输入 `locale` 查看容器中 Linux 操作系统的当前编码格式,可以看到当前编码格式是 `POSIX`:
而 `POSIX` 编码格式是不支持中文的。我们用 `locale -a` 看看容器内所有编码格式:
这里 `C.UTF-8` 是支持中文的。要想让容器内 Linux 系统支持中文编码,我们得把容器编码设置为 `C.UTF-8`。方法是启动容器或者容器启动后进入容器时,指定环境变量 `env LANG=C.UTF-8`:
docker exec -it mysql env LANG=C.UTF-8 bash
解决 Docker 这两个坑的方法是临时性的,如果我们要重新建一个容器,又得重新设置。有没有更方便的办法呢?有的,那就是用 Dockerfile 自定义一个镜像,从源头上解决问题。
(待续)