基于docker的mysql安装实战

前言

最近需要基于两台windows服务器(24核128G核80核128G)搭建数仓,数仓决定采用hadoop+spark的方式搭建。
由于操作系统是基于windows的,而数仓系统大多是基于linux,因此决定采用docker容器化进行安装。
万里长征,决定现行采用mysql安装踩点。

什么是docker

docker是一个用Go语言实现的开源项目,可以让我们方便的创建和使用容器,docker将程序以及程序所有的依赖都打包到docker container,这样你的程序可以在任何环境都会有一致的表现。

简单理解,docker通过容器化技术,将环境和应用程序一起打包运行,进而屏蔽环境差异,实现“build once, run everywhere”,同时实现快速部署。

docker的基本概念

  • dockerfile
  • image
  • container
    实际上你可以简单的把image理解为可执行程序,container就是运行起来的进程。

那么写程序需要源代码,那么“写”image就需要dockerfile,dockerfile就是image的源代码,docker就是”编译器”。

因此我们只需要在dockerfile中指定需要哪些程序、依赖什么样的配置,之后把dockerfile交给“编译器”docker进行“编译”,也就是docker build命令,生成的可执行程序就是image,之后就可以运行这个image了,这就是docker run命令,image运行起来后就是docker container。

docker的底层实现

docker基于Linux内核提供这样几项功能实现的:

NameSpace

我们知道Linux中的PID、IPC、网络等资源是全局的,而NameSpace机制是一种资源隔离方案,在该机制下这些资源就不再是全局的了,而是属于某个特定的NameSpace,各个NameSpace下的资源互不干扰,这就使得每个NameSpace看上去就像一个独立的操作系统一样,但是只有NameSpace是不够。

Control groups

虽然有了NameSpace技术可以实现资源隔离,但进程还是可以不受控的访问系统资源,比如CPU、内存、磁盘、网络等,为了控制容器中进程对资源的访问,Docker采用control groups技术(也就是cgroup),有了cgroup就可以控制容器中进程对系统资源的消耗了,比如你可以限制某个容器使用内存的上限、可以在哪些CPU上运行等等。
有了这两项技术,容器看起来就真的像是独立的操作系统了。

mysql镜像安装&运行

如果只是想跑起来,mysql docker镜像的安装和运行非常的简单

  1. docker hub中搜索mysql镜像,或者docker desktop搜索镜像,查找我们需要的mysql docker镜像,本人选择的tag为 8.0.33,tag对应的有pull命令。
  2. 运行 docker pull mysql:8.0.33下载之前搜索到的镜像。
  3. 运行命令docker run --name test -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -d mysql:8.0.33启动容器
    其中 -p 3306:3306 表示将容器内的3306桥接到本机的3306端口,-d mysql:8.0.33 为之前我们下载的镜像名称。
    要是不想命令,也可以直接在docker desktop上点击运行。

这样,一个mysql容器就运行了,但是这个容器只是试玩的,它存在以下几个问题

  • 资源未隔离【可能导致该容器占用完整个服务器的情况】
  • 存储未考虑【容器内的数据在容器重启后便会丢失】
  • 配置未优化【mysql默认配置无法满足生产要求】

因此在实际的生产部署中,我们需要解决上面三个情况

问题解决

资源隔离问题

资源方面,初步拍脑袋决定给mysql的容器为5G4核,网络的话,期望container能组成局域网,但是mysql服务最好在主机上有端口映射,这样主机可以对外提供服务。磁盘方面,则不进行限制了。

内存隔离

与操作系统类似,容器可以使用的内存包括两部分:物理内存和Swap【不理解的可以阅读深入理解swap交换分区理解及扩存了解下物理内存和swap的作用及关系】。

Docker通过下面两组参数来控制容器内存的使用量。

-m 或 –memory:设置内存的使用限额,例如:100MB,2GB。
–memory-swap:设置内存+swap的使用限额。
默认情况下,上面两组参数为-1,即对容器内存和swap的使用没有限制。如果在启动容器时,只指定-m而不指定–memory-swap, 那么–memory-swap默认为-m的两倍。

举例

1
2
3
4
5
# 允许该容器最多使用200MB的内存和100MB 的swap。
docker run -m 200M --memory-swap=300M ubuntu

# 容器最多使用200M的内存和200M的Swap
docker run -it -m 200M ubuntu

那么我们到底应该怎么设置呢?

参考redhat公司的推荐ch-swapspace

  • 内存小于4GB时,推荐不少于2GB的swap空间;
  • 内存4GB~16GB,推荐不少于4GB的swap空间;
  • 内存16GB~64GB,推荐不少于8GB的swap空间;
  • 内存64GB~256GB,推荐不少于16GB的swap空间。

本次mysql容器规划5G,那么推荐是不少于4GB,由于docker swap默认不得小于memory,因此就采用5GB,
因此最终的运行参数为

1
docker run  --name mysql  -m 5G --memory-swap=5G -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -d mysql:8.0.33

CPU 隔离

docker对cpu的隔离分为三种

  • CPU份额控制:-c或–cpu-shares
  • CPU核控制:–cpuset-cpus、–cpus
  • CPU周期控制:–cpu-period、–cpu-quota

具体这里就不进行展开了,可以阅读Docker资源(CPU/内存/磁盘IO/GPU)限制与分配指南进行了解。

本人直接采用了CPU核控制。

–cpus: 限制容器运行的核数;从docker1.13版本之后,docker提供了–cpus参数可以限定容器能使用的CPU核数。这个功能可以让我们更精确地设置容器CPU使用量,是一种更容易理解也常用的手段。

1
2
# 容器最多可以使用主机上两个CPU ,除此之外,还可以指定如 1.5 之类的小数。
docker run -it --rm --cpus=2 centos /bin/bash

由于本次规划的分配核数为4,因此最终的一个参数为

1
2
docker run  --name mysql --cpus=4  -m 5G --memory-swap=4G -p 3306:3306 -e MYSQL_ROOT_PASSWORD=root -d mysql:8.0.33

网络配置

docker网络相关的文章可以阅读全网最详细的Docker网络教程详解这篇文章进行了解。

对外提供服务,通过 -p 参数进行映射即可。

1
2
将容器内的3306端口映射到宿主机的1306端口
-p 1306:3306

容器的局域网设置

  1. 创建网络
    1
    docker network create -d macvlan  --subnet=172.16.1.0/24 --gateway=172.16.1.1 -o parent=eth0 dw-network
  2. 查看是否安装成功
    1
    docker network ls
    结果如下,可以看到dw-network网络创建完毕
    1
    2
    3
    4
    5
    6
    PS C:\Users\dell> docker network ls
    NETWORK ID NAME DRIVER SCOPE
    415ecf186909 bridge bridge local
    b01f87d53a20 dw-network macvlan local
    0ec71466bed7 host host local
    417fdf60fc8c none null local
  3. 启动容器并绑定固定ip
    1
    2
    3
    4
    5
    6
    7
    8
    9
    docker run --net=dw-network --name="test1" --ip=172.16.1.11 -e MYSQL_ROOT_PASSWORD=root -d mysql:8.0.33
    docker run --net=dw-network --name="test2" --ip=172.16.1.12 -e MYSQL_ROOT_PASSWORD=root -d mysql:8.0.33

    查看容器
    docker ps -a
    可以看到有两个容器已经启动
    CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
    569bb77f586e mysql:8.0.33 "docker-entrypoint.s…" 4 seconds ago Up 2 seconds test2
    224b3db441ae mysql:8.0.33 "docker-entrypoint.s…" 30 seconds ago Up 29 seconds test1
  4. 网络验证
    1
    2
    3
    4
    进入容器
    docker exec -it test1 /bin/bash
    docker exec -it test1 /bin/bash
    容器内telnet,yum都没,暂时就不验证了。。。。

磁盘隔离

Block IO 是另一种可以限制容器使用的资源。Block IO 指的是磁盘的读写,docker 可通过设置权重、限制 bps 和 iops 的方式控制容器读写磁盘的带宽

注:目前 Block IO 限额只对 direct IO(不使用文件缓存)有效。

这里就不进行权重限制了,后续有需求再进行配置。

如果使用的话,应该会采用权重策略。毕竟磁盘是最大的资源瓶颈。

存储持久化问题

docker内的文件都是临时的,当重启时,数据就会全部丢失。

但是docker贴心的提供了volume技术解决这一持久化问题。

即将宿主机的目录映射到容器目录。

mysql设计目录相关的,一个为/etc/my.cnf,一个为存储和日志,存储和日志默认配置都在/var/lib/mysql目录下,因此建立对应的volume映射

宿主机目录 容器目录
D:\work\docker\mysql/conf/my.cnf /etc/my.cnf
D:\work\docker\mysql/conf/ /etc/mysql
D:\work\docker\mysql/logs /var/log/mysql
D:\work\docker\mysql/data /var/lib/mysql

因此一个参数为

1
2
3
4
5
6
7
docker run --name test-mysql -p 3306:3306 \
-e MYSQL_ROOT_PASSWORD=root \
-v docker\mysql/conf/my.cnf:/etc/mysql/my.cnf \
-v docker\mysql/conf:/etc/mysql \
-v /hdocker\mysql/logs:/var/log/mysql \
-v docker\mysql/data:/var/lib/mysql \
-d mysql:8.0.33

mysql 性能配置

mysql的内存分配,参考

实战操作

铺垫了这么久,终于可以进行最后的实战操作记录了。

1.镜像下载

1
docker pull mysql:8.0.33

目录&文件准备

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
mkdir docker #docker总目录
mkdir docker/mysql # mysql容器预留目录
mkdir docker/mysql/conf # 配置持久化目录
mkdir docker/mysql/conf/conf.d # 配置持久化目录
mkdir docker/mysql/logs # 日志持久化目录
mkdir docker/mysql/data # 数据持久化目录
cat "" > docker/mysql/my-compose.yml
cat "" > docker/mysql/conf/my.cnf
编辑my.cnf,填入如下内容
[mysql]
default-character-set=utf8
[mysqld]
character-set-server=utf8
default-storage-engine=MyISAM
thread_cache_size=16
bulk_insert_buffer_size=8388608
concurrent_insert=AUTO
connect_timeout=10
delay_key_write=ON
delayed_insert_limit=100
delayed_insert_timeout=300
delayed_queue_size=1000
flush_time=0
innodb_adaptive_flushing=ON
innodb_adaptive_flushing_lwm=10
innodb_adaptive_hash_index=ON
innodb_adaptive_max_sleep_delay=150000
innodb_autoextend_increment=64
innodb_autoinc_lock_mode=2
#innodb_buffer_pool_dump_at_shutdown=ON
#innodb_buffer_pool_dump_pct=25
#innodb_buffer_pool_load_at_startup=ON
#innodb_change_buffer_max_size=25
#innodb_change_buffering=all
#innodb_checksum_algorithm=crc32
#innodb_cmp_per_index_enabled=OFF
#innodb_commit_concurrency=0
#innodb_compression_failure_threshold_pct=5
#innodb_compression_level=6
#innodb_compression_pad_pct_max=50
#innodb_concurrency_tickets=5000
#innodb_deadlock_detect=ON
#innodb_disable_sort_file_cache=OFF
innodb_flush_log_at_trx_commit=2
innodb_flush_method=O_DIRECT
innodb_flush_neighbors=0
innodb_flush_sync=ON
innodb_ft_cache_size=8000000
innodb_ft_enable_diag_print=OFF
innodb_ft_enable_stopword=ON
#innodb_ft_max_token_size=84
#innodb_ft_min_token_size=3

命令启动

创建网络

1
2
3
docker swarm init
docker network create -d overlay --attachable --subnet 172.16.1.0/24 --gateway 172.16.1.1 dw-network

容器启动

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
docker run  --name="mysql"  `
--net=dw-network `
--ip=172.16.1.11 `
--cpus=4 `
-m 5G `
--memory-swap=5G `
-v D:\work\docker\mysql/conf:/etc/mysql `
-v D:\work\docker\mysql/logs:/var/log/mysql `
-v D:\work\docker\mysql/data:/var/lib/mysql `
-p 1306:3306 `
-p 13060:33060 `
-e MYSQL_ROOT_PASSWORD=root `
-d mysql:8.0.33


docker run --name="mysql" `
--net=dw-network `
--ip=172.16.1.11 `
--cpus=4 `
-m 5G `
--memory-swap=5G `
-v D:\work\docker\mysql/conf:/etc/mysql `
-v D:\work\docker\mysql/logs:/var/log/mysql `
-v D:\work\docker\mysql/data:/var/lib/mysql `
-p 1306:3306 `
-p 13060:33060 `
-e MYSQL_ROOT_PASSWORD=root `
-d mysql:8.0.33



其中
-name="mysql" 容器名称为mysql
--net=dw-network #使用之前创建的 dw-network 网络
--ip=172.16.1.11 # 固定分配ip
--cpus=4 #分配4核cpu
-m 5G #分配5g物理内存
--memory-swap=5G #分配5g swap
-p 1306:3306 #将3306映射到主机的1306
-v D:\work\docker\mysql/conf/my.cnf:/etc/mysql/my.cnf #my.cnf配置文件
-v D:\work\docker\mysql/conf:/etc/mysql #mysql配置文件
-v D:\work\docker\mysql/logs:/var/log/mysql #日志目录
-v D:\work\docker\mysql/data:/var/lib/mysql #存储目录
-p 1306:3306 #将3306映射到主机的1306
-p 13060:33060 #将33060映射到主机的13060
-e MYSQL_ROOT_PASSWORD=root #指定mysql root默认密码
-d mysql:8.0.33 #启动的镜像

docker run -it –network dw-network –rm mysql 8.0.33 -hlocalhost -uroot -p
docker run -it –rm mysql 8.0.33 -hlocalhost -uroot -p

一些注意点

[Warning] World-writable config file ‘/etc/mysql/conf.d/my22.cnf’ is ignored.

这个报错,因为配置文件权限为777,mysql认为安全隐患较大,因此忽略。

解决方法
windows下本人未找到对应的linux权限
进入容器
修改文件权限

1
2
3
chmod 644 my.cnf


rpc error code = PermissionDenied desc = network dw-network not manually attachable.

当使用swarm的overlay网络,在该网络中运行容器时报“network xx not manually attachable”的错误

默认情况下使用docker network create -d overlay NET 创建的网络只能被swarm service使用,如果需要被独立的容器使用,需要添加–attachable选项

1
2
docker network create -d overlay --attachable my-attachable-overlay

总结

不知怎么的,以前docker看的云里雾里的东西,现在看起来也能理解了,一些踩坑点自己就能想到。

总的来说,使用docker安装应用,在有官方镜像的情况下,突出一个轻松加简单,我们只需自己做好资源的规划和配置即可。

build once,run anywhere,在docker这个技术下,很好的实现了。

香~

参考文章