共计 10039 个字符,预计需要花费 26 分钟才能阅读完成。
前言
我想了半天,该做什么我的项目,基于笔者的数据库常识羸弱,怕一方面做前端一方面做后端会搞得四不像,又累工夫又长。所以就想以做纯 API 为目标,只做接口会不会更快一些呢
注释
笔者打算做一个全唐诗的 API 我的项目,此我的项目只为学习 ruby on rails web 开发并部署至服务器,会逐渐从零开始到部署上线,部署伎俩会有些原始,不过没事,下个我的项目笔者会降级部署伎俩
先新建一个 API 我的项目
rails new --api --database=postgresql --skip-test tangpoetry
意思是新建一个唐诗的 API 我的项目,数据库为 postgresql,跳过测试
新建后进入我的项目,并更新 gem 下载源
cd tangpoetry
bundle config mirror.https://rubygems.org https://gems.ruby-china.com
从新下载依赖
bundle install --verbose # verbose:打印下载依赖过程
再去 config/database.yml
中批改开发环境时的数据库
development:
<<: *default
database: tangpoetry_dev
username: tangpoetry
password: 123456
host: localhost
在此之前,须要在 pgAdmin4(postgresql 图形界面)中创立 database、username、password 等,这里不做赘述
咱们在本地启动服务
rails s
如此,rails 利用就启动了
建设数据库
笔者要做的是全唐诗的 API 接口,要有什么性能先不说,起码不会本人做数据,在网上找了一个 唐诗的数据库,先导入 mysql 中,能看到它有两个表
咱们先依据表中的字段创立俩模型(Model)
rails g model poetry poet_id:integer content:text title:string
rails g model poet name:string
PS:模型(Model)须要是复数。id、created_at、updated_at 会本人创立
此时,就有个问题了,这个我的项目的 sql 是以 mysql 为语法而写的,怎么将它转换为 postgresql 呢?
先不要管这个问题,来设计一下要做的 API
- 随机获取一首诗:/poetry/random
- 用诗的题目查问:/poetry/title/ 静夜思
- 列出这个诗人的所有诗:/poetry/author/ 李白
- 列出这个诗人的这首诗:/poetry/author/ 张若虚 /title/ 春江花月夜
- 通过创作数量排名:/poet/list/createnum(没做)
确定好要做的 API 后,咱们就去实现,先在命令行中执行以下代码来创立控制器(Controller)
rails g controller poetry random
# rails g controller 名字 动作
会生成这样的文件:
以上命令的意思是说在创立一个名为 poetry 的类,它的办法为 random。rails 会帮你创立好类和办法以及在路由处创立一个 poetry/random
的路由
批改 poetry_controller.rb
中的内容:
class PoetryController < ApplicationController
def random
render json: {resource: 'hello, world'}
end
end
而后拜访 http://127.0.0.1:3000/poetry/random
,就能看到 json 格局的返回值了
拜访 url,利用匹配 route,routes 匹配 controller,controller 操作 model,并返回对应的数据给路由
当初咱们要回到最开始的疑难,怎么把全唐诗中的 sql 转化为 postgresql?
笔者通过一些尝试,发现能够转换,数据是有的,数据结构也统一,无非本来是用 mysql 写的,当初将其改成 postgresql。而当初咱们曾经有数据库的两张表了,只有插入数据即可,用 pgAdmin 也好,用其余工具也罢,用 postgresql 语法把数据插入数据库中
在仓库中别离提供 mysql 的数据tang_poetry.sql
和 postgresql 的数据 tangpoetry.sql
INSERT INTO poets (id,name,created_at,updated_at) VALUES (1,'李世民','2014-06-02 11:47:52','2014-06-02 11:47:52'),(...)
INSERT INTO poetries (id,poet_id,content,title, created_at, updated_at) VALUES (2,1,'塞外悲风切,交河冰已结。瀚海百重波,阴山千里雪。迥戍危战火,层峦引高节。悠悠卷旆旌,饮马出长城。寒沙连骑迹,朔吹断边声。胡尘清玉塞,羌笛韵金钲。绝漠干戈戢,车徒振原隰。都尉反龙堆,将军旋马邑。扬麾氛雾静,纪石功名立。荒裔一戎衣,灵台凯歌入。','饮马长城窟行','2014-06-02 11:47:52','2014-06-02 11:47:52'),(...)
如何导入数据:应用 pgAdmin,能够抉择要导入数据的数据库、右键单击该数据库并抉择“Restore…”选项,之后抉择要导入的数据文件并执行导入操作
实现第一个接口
这个时候,数据库中已有全唐诗的数据,咱们当初要做第一个接口,即获取一首随机诗
首先是获取随机数,其次是依据这个 id 找到这一项
...
def random
random = Poetry.find(rand(1...43030))
render json: {resource: random}
end
...
ruby on rails 就是这么简略,这样就实现了 random 接口
其余 API
除了 random 接口,咱们还有四个接口,做好后再部署唐诗 API 我的项目就实现了,堪称是半小时实现一我的项目
- 用诗的题目查问:/poetry/title/ 静夜思
- 列出这个诗人的所有诗:/poetry/author/ 李白
- 列出这个诗人的这首诗:/poetry/author/ 张若虚 /title/ 春江花月夜
- 通过创作数量排名:/poet/list/createnum
前三者都是在 poetry 路由下的拜访,咱们先新建 routes
get 'poetry/random'
+get 'poetry/title/:title', to: 'poetry#title'
+get 'poetry/author/:author', to: 'poetry#author'
+get 'poetry/author/:author/title/:title', to: 'poetry#author_title'
意思是拜访 poetry/title/:title
路由,就是去 poetry_controller
中找 title 办法,并且有个 title 变量(其余两者雷同情理)。再去 poetry_controller
文件,新建title
、author
、author_title
办法
class PoetryController < ApplicationController
def random
random = Poetry.find(rand(1...43030))
render json: {data: random}
end
# /poetry/title/ 静夜思
def title
result = Poetry.find_by(title: params[:title])
render json: {data: result}
end
# /poetry/author/ 李白
def author
author = Poet.find_by(name: params[:author])
result = Poetry.where({poet_id: author[:id]})
render json: {data: result}
end
# /poetry/author/ 张若虚 /title/ 春江花月夜
def author_title
author = Poet.find_by(name: params[:author])
result = Poetry.where({poet_id: author[:id], title: params[:title]})
render json: {data: result}
end
end
又搞定了三个接口,你就说快不快
其中,对 ORM 有所不理解,笔者是在 RailsGuides 查问
Poet.find_by(name: params[:title])
# 只能找满足条件的第一条
Poetry.where({poet_id: @author[:id]})
# where 条件查问
# 找到满足条件的所有项
接着偶们做最初一个接口,即通过创作数量排名:/poet/list/createnum
rails g controller poet list
或者不必命令行,间接在 routes.rb
上批改,并新建 poet_controller.rb
文件进行更新
——————————————
淦,卡住了
笔者这里搜到一个相干的教程,奈何 sql 根底太差,还是不会弄。这个接口就不做了
部署
本地开发完结了,现部署上线
之前咱们能在 Heroku 疾速部署,但当初它曾经要免费了,所以笔者决定部署到服务器上,思路是:
先应用本地 docker 部署服务,本地跑通后,再上传源码,通过 Dockerfile 构建运行环境,在运行环境中运行源代码
初试 tangpoetry 镜像
咱们先构建 Dockerfile,上面命令很好了解,就不过都解释
FROM ruby:3.1.3
WORKDIR /app
COPY . .
RUN bundle config mirror.https://rubygems.org https://gems.ruby-china.com
ENV RAILS_ENV=production
RUN bundle config set --local without 'development test'
RUN bundle install
ENTRYPOINT bundle exec puma
而后将它打包成镜像
docker build -t tangpoetry .
基于 tangpoetry 镜像,生成容器
docker run -d --name tangpoetry_container -p 3000:3000 tangpoetry
# -d 后盾启动容器
# --name 容器名
# -p 端口映射
咱们拜访(http://localhost:3000
)首页,是能看到 Hi 的
为了测试不便,咱们新建一个根路由,返回一个 json:{message: ‘Hi’}
然而如果拜访所写的任意接口,都会拜访不了,起因很简略,因为 production 环境下的数据库未配置,所以咱们须要再建一个容器,并将唐诗数据导入到此容器中,再通过 docker network 连贯两个容器
也就是说咱们的服务由两个容器组成(后续能够的话能够通过 docker-compose 革新)
当初本地环境用的数据库是本地下载了 postgreSQL,所以咱们须要用 docker 启动 postgresSQL 镜像数据库
创立基于 postgres 的容器
docker run -d --name db-for-tangpoetry -e POSTGRES_USER=tangpoetry -e POSTGRES_PASSWORD=123456 -e POSTGRES_DB=tangpoetry_production -e PGDATA=/var/lib/postgresql/data/pgdata -v tangpoetry-data:/var/lib/postgresql/data --network=network1 postgres:14
# -d 后盾运行
# --name 容器名字叫 db-for-tangpoetry
# -e 环境命令
# -v 数据卷
# --network 应用网络
这里的数据卷和网络都要当时建好
docker volume create tangpoetry-data
:创立 tangpoetry-data 数据卷
docker volume ls
可查看数据卷列表
docker network create network1
创立 network1 网络
docker network ls
可查看网络列表
进入(postgresSQL)数据库容器
docker exec -it db-for-tangpoetry bash
连贯 tangpoetry_production 数据库
psql -h localhost -p 5432 -U tangpoetry tangpoetry_production
命令 \l
查看数据库中的表
阐明咱们的数据库创立胜利,当初咱们须要导出本地数据库,并导入到 docker 镜像数据库中
先将本地的数据库导出
pg_dump -U tangpoetry -d tangpoetry_dev > tangpoetry.sql
# pg_dump 导出数据
# -U 用户名
# -d 数据库
再导入到db-for-tangpoetry
容器中
docker exec -i db-for-tangpoetry pg_restore -U tangpoetry -d tangpoetry_production < tangpoetry.sql
# pg_restore 导入数据
笔者输出后显示的如下:
从新编译 tangpoetry 镜像
咱们须要将 tangpoetry 的源码在批改下,配置 config/database.yml
中的 production:
production:
<<: *default
database: tangpoetry_production
username: tangpoetry
password: <%= ENV["DB_PASSWORD"] %>
host: <%= ENV["DB_HOST"] %>
再从新打包(再此之前先删除原来的容器和镜像)
docker build -t tangpoetry .
基于 tangpoetry 镜像打包 tangpoetry 容器
docker run -d --name tangpoetry_container -p 3000:3000 -e DB_HOST=db-for-tangpoetry -e DB_PASSWORD=123456 --network=network1 tangpoetry
在容器中创立数据库,并 migrate
docker exec -it tangpoetry_container bin/rails db:create db:migrate
这样再拜访 3000 端口时,咱们就能看到数据了
线上部署
以上咱们曾经测试胜利了本地 docker 部署,先将它推到近程 git 仓库,后续咱们会登录服务器,并 git pull 它,而后构建 tangpoetry 镜像,由此创立 tangpoetry_container 容器
这里遇到的坑让我白了四根头发,第三个问题困扰了我两天并白了两根为数不多的头发
问题一:ruby-china 443 证书过期
bundle install 时,gems 源会 443,提醒相似这样:
Retrying download gem from https://gems.ruby-china.com/ due to error (2/4): Gem::RemoteFetcher::FetchError Net::OpenTimeout: Failed to open TCP connection to gems.ruby-china.com:443 (execution expired) (https://gems.ruby-china.com/gems/racc-1.6.2.gem)
换成阿里源、清华源都不行,笔者第一次部署时「应用不换源」来解决,等了良久才下载好,起初看到这篇文章,先将本地的依赖下载好成缓存,再 bundle 时就从本地拿就好
简略来说就两步:
先在我的项目根目录下执行以下命令
bundle cache
bundle lock --add-platform x86_64-linux
bundle package --all-platforms
(以上皆为局部截图)
再批改 Dockerfile
...
# 增加缓存到 app 中,这里其实是做了 docker 打包的优化,不做过多介绍
ADD vendor/cache /app/vendor/cache
...
# 通过本地下载依赖
RUN bundle install --local
...
当咱们很快打包后 tangpoetry 镜像后,咱们就以它为根据来构建服务,这里咱们复制本地部署时的代码docker run -d --name tangpoetry_container -p 3000:3000 -e DB_HOST=db-for-tangpoetry -e DB_PASSWORD=123456 --network=network1 tangpoetry
,先做测试
此时,咱们的服务器上的 postgres 容器还没创立,咱们先把 ruby on rails 服务部署胜利了,再连贯数据库
问题二:secret_key_base 的报错
但拜访服务器 ip+ 端口,发现拜访不了
通过 docker logs tangpoetry_container
查看报错日志
说 production 环境下的 secret_key_base 不存在
淦,又有个知识点
什么是 secret_key_base?为什么须要这个?为什么本地部署时没呈现这个?
Rails 在我的项目初始化的时候就会在根目录config
下生成 master.key
和 credentials.yml.enc
两个文件,前者能够了解为外围密钥,后者是通过 Rails 自带的加密办法生成的加密后的数据文件
关系为:
master.key + keys => encrypted
encrypted + master.key => keys
keys 是什么,你须要加密的数据,例如 secret_key_base
咱们在临时文件中的写入咱们的 keys,保留敞开后会生成一个新的 master.key
和 credentials.yml.enc
,并且临时文件会主动删除,把.enc
存在 git 中,master.key
排除在 git 外,这样,他人即便拿到源码,拿不到你的 keys(短少 master.key
解不了)
如何读取 keys 呢?
rails c
# 在命令行中输出 rails c 或者 rails console
# 输出代码
Rails.application.credentials.config # 查看所有的 keys
Rails.application.credentials.secret_key_base #查到 secret_key_base
如何批改 keys 呢?
笔者应用的是 window,应用 window 自带的 PowerShell,它能长期写进参数
$env:EDITOR="code --wait"
rails credentials:edit
此时会生成一个临时文件,咱们将 demo:12345
批改为 demo:123456
,保留并删除临时文件,会发现文件 credentials.yml.enc
产生了变动
也就是说 master.key + keys
会生成一个新的credentials.yml.enc
同理,咱们不能在本地和生成应用一套 keys,Rails 反对多环境密钥
$env:EDITOR="code --wait"
rails credentials:edit --environment production
会失去两个文件:
config/credentials/production.key(被退出 .gitignore)config/credentials/production.yml.enc
咱们只须要把 production.key
写进服务器环境变量中,就能解决问题二的问题了
$env:RAILS_ENV="production"
rails c
Rails.application.credentials.secret_key_base
最佳实际是什么?
- 先删除
master.key
和credentials.yml.enc
,通过rails credentials:edit
生成一个新的 key 和 enc,复制临时文件中的secret_key_base
- 再
rails credentials:edit --environment production
生成生产环境的临时文件,粘贴上一步的secret_key_base
- 再删除
master.key
和credentials.yml.enc
,再生成一个新的 key 和 enc
如此,再服务器上将 RAILS_MASTER_KEY 写进环境变量中,
vim ~/.bash_profile
echo DB_HOST=db-for-tangpoetry
echo DB_PASSWORD=123456
echo RAILS_MASTER_KEY=f78c0868148ca3b1aa64ee9e82c66ef4
执行 source ~/.bash_profile
立刻失效
再次启动容器,此时将 DB_HOST 等用变量模式写入
docker run -d --name tangpoetry_container --network network1 -p 3000:3000 -e DB_HOST=$DB_HOST -e DB_PASSWORD=$DB_PASSWORD -e RAILS_MASTER_KEY=$RAILS_MASTER_KEY tangpoetry
问题三:利用容器连贯不上数据库容器
在此之前,咱们曾经能在服务器 ip+ 端口上能拜访到首页,然而此时咱们还没启动数据库,所以还拜访不到数据库
咱们先启动数据库容器
docker run -d --name $DB_HOST --network network1 -p 5432:5432 -e POSTGRES_USER=tangpoetry -e POSTGRES_PASSWORD=$DB_PASSWORD -e POSTGRES_DB=tangpoetry_production -e PGDATA=/var/lib/postgresql/data/pgdata -v tangpoetry-data:/var/lib/postgresql/data postgres:14
并导入数据
docker exec -i db-for-tangpoetry pg_restore -U tangpoetry -d tangpoetry_production < tangpoetry.sql
回到利用容器,进入容器中,初始化数据库
docker exec -it tangpoetry_container bash # 进入唐诗容器
# 进入容器后
rails db:create
发现拜访不了
也就是说容器之间的拜访成了问题
笔者找了很多材料,找了两天还是没有解决问题,也在 ruby china 上发问,终于在在这篇问答中找到了灵感,我降级了 docker 版本,从 19 降级到了 23,就解决了
以上的命令用以下命令就能实现
docker exec tangpoetry_container bash db:create db:migrate
如此,咱们的我的项目就算胜利上线了
如有问题可拜访我的项目地址:https://github.com/johanazhu/tangpoetry
后续
如果,我是说如果,咱们心愿加上随机一页的成果,或者说每天更新一首诗,本地开发,胜利,推到 git 仓库,并在服务器上删除原有镜像,生成新镜像,再依据新镜像打包
要是我的项目迭代频繁,会不会感觉,好麻烦
这篇文章花了笔者很多工夫,好不容易才上线
如当初 2023 年 6 月份,间隔笔者实现初稿曾经 3 个月,笔者也找到了新的收费的部署形式——fly.io
参考资料
- Active Record 根底
- 山竹记账全栈版 Vue 3 + Rails 7+TSX
- 山竹记账免费版
- How to run ‘rails credentials:edit’ on Windows 10 without installing a Linux Subsystem
- Why can’t I connect to Postgres in Docker?
- Postgresql9.1 suddenly could not connect to server: No route to host
- Capistrano: ArgumentError: Missing secret_key_base for ‘production’ environment, set this string with bin/rails credentials:edit
- How to get a Docker container’s IP address from the host
- Rails + PostgreSQL + Docker
- caching-all-native-gem-platforms
系列文章
- 前端学 Ruby:前言
- 前端学 Ruby:装置 Ruby、Rails
- 前端学 Ruby:相熟 Ruby 语法
- 前端学 Ruby:相熟 Rails