痛点说明
当我们在使用 debian/ubuntu 的时候,如果需要安装一些应用,通常会执行 sudo apt install ...
,如果源中有的话,这是最傻瓜式,也最方便的方式了。
但是笔者其实一直不是很喜欢这种方式,原因如下:
这种方式由于给到了 root 权限,无形中,背后做了很多事,对于笔者来说,总感觉缺乏一些掌控
这种方式安装的软件,相关的文件会分散到各种目录,很乱。
没法精确的去控制到底使用哪个版本,因为源中可能没有。
为什么乱呢? 这和 Linux 的设计有关。这块笔者不是很清晰,之前看过一片 文章 ,这篇文章很长,它后面简要介绍了 Linux 上安装软件的特点,强烈建议玩 Linux 的同学看下。这里,我简单描述下,希望我理解的没有问题。
我们先说下 Windows。 在 Windows 下,安装一个软件,通常会是一个安装包,点击安装,会指定一个安装目录,安装完成后,这个软件连同这个软件的相关依赖,都会同时放进我们指定的安装目录。
当然,如果是那种解压直接用的,直接找个目录放解压就好。这个时候,是不是很清晰,某个软件就是安装在某个目录。当然,除此外,然后也有可能在 比如 User 目录下,放一些配置文件,缓存什么的。但是笔者以为,相比较 Linux,可以说相当清爽。
反观 Linux 呢? 当我们执行 sudo apt install ...
时,最终文件会散落到 /usr
、/var
、/run
、/etc
等等这些目录,如果你是个小白,可能对此完全蒙圈,即便对于一些开发人员,其实对这些也知之不多。只有对于有经验的运维人员,才能快速熟练的找到相关文件的位置。
我们从 Linux 上面软件的安装方式出发,其实就能窥见这其中的混乱了。 比如说 Ubuntu,当我们要安装一个软件时,通常会有几种安装方式:
所以,在使用 Linux 的时候,我们总会去搜索诸如 “Ubuntu18.04 如何安装 mongodb ” 这样的问题,而且经常是一个软件有几种不同的安装方式,对于初学者,当从其中一个点出发的时候,基本是蒙圈的。与此同时,我们在使用 Windows 完全不会有这样的困扰,管你是 QQ,PS 还是 mysql,基本上通过一个安装包解决问题。
下面我们来梳理下混乱之下到底是些什么?
以下描述基于 Ubuntu18.04 安装 redis 为例, 当我们执行 sudo apt install redis
的时候,系统做了如下事件:
创建了 redis
user 和 redis
group, 同时指定其 home 为 /var/lib/redis
,我们 执行 cat /etc/passwd | grep redis
, 看到以下结果,注意该用户无法登录系统
复制 redis:x:124:129::/var/lib/redis:/usr/sbin/nologin
添加了 /etc/init.d/redis-server
的可执行脚本
添加了 /etc/rcx.d/S01redis-server
的软链
添加了 /lib/systemd/system//redis-server.service
文件,供 systemd 调度
添加了 /etc/systemd/system/multi-user.target.wants/redis-server.service -> /lib/systemd/system/redis-server.service
的软链,用于开机启动
将 redis-server
、redis-cli
等命令 放到 /usr/bin
目录下
启动 redis-server 服务,这个时候会关联一下目录
/etc/redis/redis.conf
redis 默认配置文件
/var/lib/redis
redis 用户家目录,默认情况下,redis 的数据会放在这里
/var/log/redis
默认 redis 的日志会放在这里
/run/redis.pid
默认 redis daemon 的 pid 放在这里
如果这个软件按照规范来的话,通常是做了这些事情。当然不同的软件会有所差别,这个差别可能是未知的,你如果想要搞明白,需要去花好多时间研究。
作为一个工程师,笔者以为信心源于全面的测试和对细节的掌控,所以以上的安装方式,我其实是惧怕的。
出于以上种种原因吧,笔者更倾向于下载二进制文件解压,尽量像在 Windows 下,采用单一目录安装。其实这种安装方式,在 Linux 下也越发的常见了,比如 hadoop 生态组件,比如 elasticsearch 生态的组件,都是采用这种方式安装的。
我们来看下两种安装方式目录结构的区别
apt install
的方式,这种方式将应用程序相关文件拆开,放到 Linux 下的各种目录下,其实这种方式我觉得类似于将一个项目完全从纯技术角度分包,当项目小的时候还好,但是项目很大时,一切都会变得恶心。目录结构如下:
复制 # tree /var/log
/var/log
├── redis
│ └── redis-server.log
├── mongod
│ └── mongod.log
# tree /var/lib
/var/lib
├── redis
│ └── .bashrc
├── mongod
│ └── .bashrc
# tree /run
/run
├── redis
│ └── redis.pid
├── mongod
│ └── mongod.pid
# tree /etc
/etc
├── redis
│ └── redis.conf
├── mongod
│ └── mongod.conf
第二种方式是先将某软件相关的文件聚合到自己的目录下,然后在自己的目录下,从技术角度再分包。我们采用这种方式组织文件。其实也体现了内聚的思想。类似于,将整个项目根据不同的 feature,先分包,然后在 feature 包内,再从技术角度出发细分。如果项目足够复杂,当然这种是更好的。其目录结构如下:
复制 # tree /opt
/opt
├── redis
│ ├── data
│ │ └── redis.data
│ ├── logs
│ │ └── redis.log
│ ├── redis.conf
│ ├── redis.pid
├── mongodb
├── data
│ └── mongod.data
├── logs
│ └── mongod.log
├── mongod.conf
├── mongod.pid
部署 mongodb
下面,我们以 mongodb 为例,在 Ubuntu18.04 上详细的走一遍流程。mongodb 官方提供了二进制文件下载,在这里 。
下面我先说下我自己的部署标准
相关的可执行文件如 mongo
、mongod
全局可用
了解了以上思路,我们正式开始部署流程,注意以下所有操作都是在 root 账户下,后面不再赘述。
1. 先解压从官网下载的二进制文件包,在 /opt
目录下解压后,解压后文件目录如下
复制 # tree /opt
/opt
└── mongodb-linux-x86_64-3.6.4
├── bin
│ ├── bsondump
│ ├── install_compass
│ ├── mongo
│ ├── mongod
│ ├── mongodump
│ ├── mongoexport
│ ├── mongofiles
│ ├── mongoimport
│ ├── mongoperf
│ ├── mongoreplay
│ ├── mongorestore
│ ├── mongos
│ ├── mongostat
│ └── mongotop
├── GNU-AGPL-3.0
├── MPL-2
├── README
└── THIRD-PARTY-NOTICES
2. 创建相关文件及文件夹
复制 # 进入到 mongodb 安装目录
cd /opt/mongodb-linux-x86_64-3.6.4
# 创建将来存放 mongo 数据的文件夹
mkdir data
# 创建日志文件夹
mkdir logs
# 创建并编写配置文件
vim mongod.conf
# 创建并编写 mongod.service, 用于将来注册进 systemd
vim mongod.service
配置文件 mongod.conf 内容如下
复制 # mongod.conf
# Where to store the data.
dbpath = /opt/mongodb/data
storageEngine = wiredTiger
directoryperdb = true
# where to log
logpath = /opt/mongodb/logs/mongodb.log
logappend=true
logRotate = reopen
port = 27017
# daemon
fork = true
# Enable journaling, http://www.mongodb.org/display/DOCS/Journaling
journal=true
# Turn on/off security. Off is currently the default
noauth = true
# auth = true
# keyFile = /srv/mongodb/conf/rs.key
# crawler repl
# replSet=rs_crawler
pidfilepath = /opt/mongodb/mongod.pid
Service 配置文件 mongod.service 内容如下
复制 # mongod.service
[Unit]
Description=mongodb server
After=network.target
[Service]
Type=simple
User=mongodb
Group=mongodb
SyslogIdentifier=mongodb
LimitNOFILE=65536
ExecStart=/opt/mongodb/bin/mongod -f /opt/mongodb/mongod.conf
ExecStop=/bin/kill -s TERM $MAINPID
PIDFile=/opt/mongodb/mongod.pid
Restart=always
[Install]
WantedBy=multi-user.target
3. 接下来为了解耦或是方便,我们可以创建一些软链接
复制 ln -s /opt/mongodb-linux-x86_64-3.6.4 /opt/mongodb
ln -s /opt/mongodb-linux-x86_64-3.6.4/bin/mongod /usr/local/bin/mongod
ln -s /opt/mongodb-linux-x86_64-3.6.4/bin/mongo /usr/local/bin/mongo
# /opt/mongodb-linux-x86_64-3.6.4/bin 下的其它命令是否创建软链,根据自己需求
我们注意到,mongodb 的所有相关文件都在自己的目录内,如果外部引用的话,都是通过软链接的方式,这类似于设计模式中的桥接或是代理。
完成以上步骤后, 执行 mongod -f /opt/mongodb/mongod.conf
, 启动 mongodb,如果一切正常的话,mongodb 正常启动,此时目录结构如下,所有相关的内容全部聚合到一个目录下。
怎么样,很清晰吧 O(∩_∩)O
复制 # tree /opt
/opt
├── mongodb -> mongodb-linux-x86_64-3.6.4
└── mongodb-linux-x86_64-3.6.4
├── bin
│ ├── bsondump
│ ├── install_compass
│ ├── mongo
│ ├── mongod
│ ├── mongodump
│ ├── mongoexport
│ ├── mongofiles
│ ├── mongoimport
│ ├── mongoperf
│ ├── mongoreplay
│ ├── mongorestore
│ ├── mongos
│ ├── mongostat
│ └── mongotop
├── data
│ ├── admin
│ │ ├── collection-0--3307101973996970816.wt
│ │ └── index-1--3307101973996970816.wt
│ ├── diagnostic.data
│ │ ├── metrics.2019-10-15T11-05-02Z-00000
│ │ └── metrics.2019-10-15T11-05-20Z-00000
│ ├── journal
│ │ ├── WiredTigerLog.0000000002
│ │ ├── WiredTigerPreplog.0000000001
│ │ └── WiredTigerPreplog.0000000002
│ ├── local
│ │ ├── collection-2--3307101973996970816.wt
│ │ └── index-3--3307101973996970816.wt
│ ├── _mdb_catalog.wt
│ ├── mongod.lock
│ ├── sizeStorer.wt
│ ├── storage.bson
│ ├── WiredTiger
│ ├── WiredTigerLAS.wt
│ ├── WiredTiger.lock
│ ├── WiredTiger.turtle
│ └── WiredTiger.wt
├── GNU-AGPL-3.0
├── logs
│ └── mongodb.log
├── mongod.conf
├── mongod.pid
├── mongod.service
├── MPL-2
├── README
└── THIRD-PARTY-NOTICES
通过 mongod -f /opt/mongodb/mongod.conf
启动的服务在后台运行,通过 ps
找到 pid 先将其杀掉,然后进行下一步,否则会导致后续配置失败。
4. 注册到系统服务
复制 # 创建 mongodb 的用户,通常这种系统服务,出于安全考虑,我们不会直接以 root 用户运行
# 我们首先创建一个 mongodb 用户,将来用这个用户运行 mongodb
# 下面的命令创建了 mongodb user,同时创建了 mongodb group,指定该用户无法登录系统,同时创建了家目录,此处家目录其实可以不创建,因为我们的部署流程中没用到 mongodb 的家目录
useradd mongodb --home-dir /var/lib/mongodb --shell /usr/sbin/nologin --create-home --user-group
# 将 mongodb 的安装目录 全部交给 mongodb 用户
chown -R mongodb:mongodb /opt/mongodb /opt/mongodb-linux-x86_64-3.6.4
# 将 service 文件软链到 /lib/systemd/system, 注册成系统服务
ln -s /opt/mongodb/mongod.service /lib/systemd/system/mongod.service
# 重载 systemd 列表
systemctl daemon-reload
# 清除掉刚刚启动 mongodb 在安装目录下创建的文件,因为刚才是以 root 用户启动的,里面创建的文件都属于 root,不删除的话,服务起不来
rm -rf /opt/mongodb/mongod.pid /opt/mongodb/logs/* /opt/mongodb/data/*
# 开启服务, 如果一切正常,显示如下
systemctl start mongod.service
● mongod.service - mongodb server
Loaded: loaded (/opt/mongodb/mongod.service; disabled; vendor preset: enabled)
Active: active (running) since Tue 2019-10-15 19:29:10 CST; 2s ago
Main PID: 21850 (mongod)
Tasks: 23 (limit: 478)
CGroup: /system.slice/mongod.service
└─21850 /opt/mongodb/bin/mongod -f /opt/mongodb/mongod.conf
Oct 15 19:29:10 iZ8vb9wvg14ypn9rjj3sdpZ systemd[1]: Started mongodb server.
# 设置开机启动
systemctl enable mongod.service
结语
至此,我们尽量模拟了 apt install ...
的方式部署好了 mongodb,并逐步拆解成单一步骤,感觉一切尽在掌握。
但是其实,其中还是有些不同的。
上面我们在注册系统服务的时候提到了 systemd, init.d, 还用到了 systemctl
命令, 这到底是什么呢?
其中的不同恰好涉及到这些知识点,我们 下篇文章 详细说明。