运行样例代码是学习新框架和工具的最佳方法
学习一个新框架或者工具的最好办法就是运行并调试和修改别人写的样例代码。我们可以通过阅读博客寻找推荐,或者直接从Github上面搜索,来找到感兴趣的项目,拉到本地运行。但是找到的项目并不一定都对初学者友好,可能没有充分考虑初学者的知识基础,导致在本地运行时遇到各种各样的问题。这篇文章总结了一些我作为Docker、MongoDB的初学者,在本地跑通ShortURL项目过程中遇到的一些问题和技巧。
运行样例代码前先搞明白前置条件
我看到项目的README版本默认了开发者明白Docker的用法,并且能够自己启动MongoDB,并配置好.env文件。在执行docker compose up --build前,需要搞明白这三个方面。
“在做中学,在学中做”才更高效
为了能以尽量短的时间运行起这个项目,我们不必要从头到尾学习每一项技术,而是通过学习理解新概念、执行命令、解决错误的方式来达成目的。始终谨记我们的目标是在尽可能短的时间内先跑通,至于内部的细节,可以跑通后慢慢钻研。
在做中学,在学中做,是最快的达成目标的路径。不能只理解概念,不执行命令,不解决错误。也不能陷在执行命令和解决错误中,不思考,不理解概念。我们执行的每一步都是充分考虑清楚的,并且有过充分验证的。
通过做与类比来理解Docker的相关概念
要运行这个项目,我要先能把Docker在本地跑起来。Docker的核心是两个概念,一个是镜像(Image),另一个是容器(Container),这是站在运行时和软件的编译产物角度看问题。对于程序员,我们还要站在编译时的角度看,要知道脚本或者命令如何控制最终的运行效果。
我先下载了Docker Desktop的MacOS版本。安装后,照着学习中心的两个初始教程,《如何运行一个容器》、《运行Docker Hub上的镜像》,在本地成功通过localhost:8089打开了一个网页。
我理解镜像就是对编译产物做版本控制,从形式上看,就如同一个压缩包,从内容上看,也不必要研究文档,直接在Docker Desktop的用户界面上,点击一个镜像,看到其中有Image hierarchy、Layers、Images、Vulnerabilities和Packages,一共五项。顾名思义,Images包含了当前的镜像依赖的镜像有几个,而Image hierarchy描述了依赖关系。Layers中包含了具体的执行命令,大概就是镜像在容器中运行时候要执行的内容。Packages是Images中详细的可执行文件和库文件。Vulnerabilities是一些安全漏洞。初始新概念的理解必然是有偏差的和错误的,但只要不妨碍我们的终极目标就好。
对于容器,我把它理解成一个运行环境,像一台虚拟的系统一样,可以远程登录,查看日志,执行命令,跟宿主机器互相传递文件等等。
对镜像和容器的控制是通过Dockerfile和docker命令来实现的。每个镜像都是从Dockerfile构建得来的。每个容器都需要通过docker命令进行操作。通过docker help可以看到对镜像和容器能做哪些控制。
看到ShortURL项目中还有docker-compose.yml文件,大概也能猜到意思是为了管理启动多个容器。这里面就有容器之间的依赖关系、网络访问、持久化、参数配置等基本问题。
看docker-compose.yml的services中声明了node-server、client-server、redis-server、zookeeper-server、nginx一共五个服务,显然没有数据库MongoDB的声明。观察depends_on了解到依赖关系中只有nginx对node-server的依赖。
通过运行和测试来理解MongoDB的相关概念
我先尝试在Docker容器外部安装启动MongoDB,学习了解MongoDB的基本概念、控制方式和运行状态。
执行以下命令,在MacOS上安装MongoDB。
brew tap mongodb/brew
brew install mongodb-community@7.0
尝试启动服务。
mongod --config /usr/local/etc/mongod.conf
遇到openssl依赖错误,也不要慌,把路径设置对了就可以解决。
ln -sfn /usr/local/opt/openssl@1.0/ /usr/local/opt/openssl
更好的办法其实是通过Docker来测试和学习MongoDB。在docker-compose.yml中通过Command+/快捷键多行注释掉所有其他服务,并声明MongoDB服务。
mongo:
image: mongo
restart: always
container_name: mongo
ports:
- 27017:27017
environment:
MONGO_INITDB_ROOT_USERNAME: admin
MONGO_INITDB_ROOT_PASSWORD: admin
volumes:
- ./db_data/:/data/db/
MongoDB是一种数据库。数据的核心概念是围绕索引和查询展开的。一般都会是客户端服务器架构。我们通过命令行的方式,可以连上服务器进行测试。
执行mongosh,发现可以连上本地的MongoDB服务。要连Docker容器中的MongoDB需要执行下面的命令。
docker exec -it mongo mongosh -u admin -p admin --authenticationDatabase admin
在mongosh中,可以执行MongoDB Shell语言进行增、删、查、改等操作。操作的对象是文档,而文档放在数据库中。
执行show dbs,可以罗列所有数据库。执行use shorturl,可以切换数据库,到shorturl。因为数据库shorturl不存在,再次执行show dbs,也并不会罗列出它。需要在其中插入至少一个文档。通过命令db.user.insert({})来实现文档的插入。
至此,我大概了解了MongoDB的相关概念。接下来就是要让node应用来连上数据库。我尝试了修改mongod.conf的bindIP的方法,并没有成功。最终还是通过把MongoDB跟其它Docker容器放在一个子网当中,然后利用容器名做域名才配置成功,具体配置等接下来说.env的时候再详说。
在本地大胆运行命令不要怕错误和崩溃
我最初并不知道要把.env放在哪个目录下。简单一想,无非两个地方,一个是与docker-compose.yml同一级的目录,另一个是server目录下。既然文档没有说明,那就都试试。放在第一个地方,结果Redis和MongoDB都连不上,放在第二个地方,结果只有MongoDB连不上。说明.env放对了地方,并且Redis的参数配对了,而MongoDB的参数不对。继续修改尝试,发现以下配置可以通过测试。错误和崩溃说明我们尝试的方向不对,这时就要及时切换方向。一次只有一个因素变动,这样切换方向的时候就容易控制。
REACT_APP_MONGODB_URI='mongodb://admin:admin@mongo:27017/shorturl?authSource=admin'
REACT_APP_REDIS_PORT=6379
REACT_APP_REDIS_HOST='redis-server'
最终成功在http://localhost:3000的界面下,将长链接www.mongodb.com/docs/manual…生成了短链接http://localhost:4000/url/e1nt。