# 참고 URL : https://cprimozic.net/blog/rust-rocket-cloud-run/
Deploying a REST API with Rust, Diesel, Rocket, and MySQL on Google Cloud Run - Casey Primozic's Homepage
cprimozic.net
# terminal 에서 실행
1
|
cargo install diesel_cli --no-default-features --features mysql
|
cs |
## EROOR
1
2
3
4
|
error: failed to compile `diesel_cli v1.4.1`, intermediate artifacts can be found at `/tmp/cargo-installl2UFJW`
Caused by:
could not compile `diesel_cli` due to previous error
|
cs |
## EROOR - sol
### 참조 : https://github.com/diesel-rs/diesel/issues/2026
### terminal
1
2
|
sudo apt install libpq-dev libmysqlclient-dev
cargo install diesel_cli --no-default-features --features mysql
|
cs |
# Test Query : create table
1
2
3
4
5
|
CREATE USER 'rocket'@'%' IDENTIFIED BY 'your_password_here';
CREATE DATABASE rocket_app;
GRANT ALL PRIVILEGES ON rocket_app.* TO rocket;
|
cs |
# create project
1
|
cargo new diesel_demo
|
cs |
# Cargo.toml 에 입력
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
|
[package]
name = "diesel_demo"
version = "0.1.0"
authors = ["user name"]
edition = "2021"
# See more keys and their definitions at https://doc.rust-lang.org/cargo/reference/manifest.html
[dependencies]
# Powerful date and time functionality
chrono = { version = "0.4.6", features = ["serde"] }
# For connecting with the MySQL database
diesel = { version = "1.4.2", features = ["chrono"] }
# Lazy static initialization
lazy_static = "1.3.0"
# Rocket Webserver
rocket = "0.4.0"
rocket_contrib = { version = "0.4.0", features = ["json", "diesel_mysql_pool"] }
# Serialization/Deserialization
serde_json = "1.0.39"
serde = "1.0.90"
serde_derive = "1.0.90"
|
cs |
# diesel.toml 생성 후 입력
1
2
3
4
5
|
# For documentation on how to configure this file,
# see diesel.rs/guides/configuring-diesel-cli
[print_schema]
file = "src/schema.rs"
|
cs |
---------------------------------------------------- ERROR ----------------------------------------------------
# Project의 root directory에서 terminal로 실행
1
|
$ export DATABASE_URL="mysql://rocket:your_chosen_password@example.com/rocket_app"
|
cs |
1
|
$ diesel setup # Creates `migrations` directory + `src/schema.rs` file
|
cs |
--ERROR : Creating migrations directory at: /home/user/rust-test/diesel_demo/migrations
Creating database: rocket_app
Can't connect to MySQL server on 'example.com:3306' (101)
1
|
$ export DATABASE_URL="mysql://rocket:your_chosen_password@localhost/rocket_app"
|
cs |
1
|
$ diesel setup # Creates `migrations` directory + `src/schema.rs` file
|
cs |
--ERROR : Creating database: rocket_app
Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
# 폴더 생성
1
|
$ sudo apt-get --reinstall install mysql-server
|
cs |
# 심볼릭 링크 생성
1
|
$ sudo ln -s /private/tmp/mysql.sock /var/run/mysqld/mysqld.sock
|
cs |
1
|
$ export DATABASE_URL="mysql://rocket:your_chosen_password@localhost/rocket_app"
|
cs |
1
|
$ diesel setup # Creates `migrations` directory + `src/schema.rs` file
|
cs |
--ERROR : Creating database: rocket_app
Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
#mysql 중지
1
|
$ sudo service mysql stop
|
cs |
# mysql 재시작
1
|
$ sudo service mysql start
|
cs |
- * Starting MySQL database server mysqld [fail]
# mysql 접속 확인 [ubuntu]
- 참고 URL : https://ujin-dev.tistory.com/15
1
|
$ sudo /usr/bin/mysql -u root -p
|
cs |
-- ERROR : ERROR 2002 (HY000): Can't connect to local MySQL server through socket '/var/run/mysqld/mysqld.sock' (2)
- 참고 URL(1) : https://freestrokes.tistory.com/43
- 참고 URL(2) : https://technote.kr/32
해결!
---------------------------------------------------- ERROR ----------------------------------------------------
# 해결!!!
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
|
# 서비스 종료
$ sudo service mysql stop
# 데몬 실행
$ sudo /usr/bin/mysqld_safe --skip-grant-tables &
#var/run/mysqld 경로가 존재하는지 확인해주고 없다면 생성 후 권한 설정
$ sudo mkdir -p /var/run/mysqld
$ sudo chown -R mysql:mysql /var/run/mysqld
# 다시 MySQL 데몬을 실행
$ mysql -u root mysql
#mysql 접속 확인 후 user 확인
mysql> use mysql;
# 결과 : Database changed
# user 조회
mysql> select user, host from user;
# ctrl + z mysql 나오기
# Mysql 사용자 추가/생성
# 사용자 생성/추가를 위해 mysql에 root로 접속.
$ mysql -u root -p mysql
# rocket 생성
mysql> CREATE USER 'rocket'@'localhost' IDENTIFIED BY 'your_password_here';
# rocket_app 데이터베이스 생성
mysql> CREATE DATABASE rocket_app;
# 권한 설정
mysql> GRANT ALL PRIVILEGES ON rocket_app.* TO rocket;
### error : ERROR 1410 (42000): You are not allowed to create a user with GRANT
mysql> GRANT ALL PRIVILEGES ON rocket_app.* TO 'rocket'@'localhost' WITH GRANT OPTION;
# diesel에게 데이터 베이스 연결하는 방법 알려주기
# 프로젝트 root로 접근
# ../[생성한 프로젝트 명]
# ../diesel_demo
$ export DATABASE_URL="mysql://rocket:your_chosen_password@localhost/rocket_app"
# 마이그레이션을 시작하기 위한 루트 디렉터리에서 실행
$ diesel setup # Creates `migrations` directory + `src/schema.rs` file
$ diesel migration generate initialize
# 결과
# -> Creating migrations/2021-12-02-085626_initialize/up.sql
# -> Creating migrations/2021-12-02-085626_initialize/down.sql
|
cs |
# up.sql
1
2
3
4
5
6
7
8
9
|
CREATE TABLE `rocket_app`.`pageviews` (
`id` BIGINT NOT NULL AUTO_INCREMENT,
`view_time` DATETIME NOT NULL DEFAULT CURRENT_TIMESTAMP,
`url` VARCHAR(2083) NOT NULL,
`user_agent` VARCHAR(2083) NOT NULL,
`referrer` VARCHAR(2083) NOT NULL,
`device_type` TINYINT NOT NULL DEFAULT '0',
PRIMARY KEY (`id`)
);
|
cs |
# down.sql
1
|
DROP TABLE `rocket_app`.`pageviews`;
|
cs |
# 마이그레이션 run
1
|
$ diesel migration run
|
cs |
-- 결과 : Running migration 2021-12-02-085626_initialize
# 마이그레이션 단계 테스트
1
|
$ diesel migration redo
|
cs |
# src/schema.rs 자동 생성
1
2
3
4
5
6
7
8
9
10
11
|
table! {
pageviews (id) {
id -> Bigint,
view_time -> Datetime,
url -> Varchar,
user_agent -> Varchar,
referrer -> Varchar,
device_type -> Tinyint,
}
}
|
cs |
# src/main.rs 생성
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 |
#![feature(proc_macro_hygiene, decl_macro)]
extern crate chrono;
#[macro_use]
extern crate diesel;
#[macro_use]
extern crate rocket;
#[macro_use]
extern crate rocket_contrib;
extern crate serde;
extern crate serde_json;
#[macro_use]
extern crate serde_derive;
pub mod cors;
pub mod models;
pub mod routes;
pub mod schema; // Ignore errors from this for now; it doesn't get created unti later
// This registers your database with Rocket, returning a `Fairing` that can be `.attach`'d to your
// Rocket application to set up a connection pool for it and automatically manage it for you.
#[database("rocket_app")]
pub struct DbConn(diesel::MysqlConnection);
fn main() {
rocket::ignite()
.mount("/", routes![ // roustes.rs 파일 생성 후 추가
routes::index,
routes::create_page_view,
routes::list_page_views,
])
.attach(DbConn::fairing())
.attach(cors::CorsFairing) // Add this line cors.rs 파일 생성 후 추가
.launch();
}
|
cs |
|
|
# src/models.rs 생성
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
|
use chrono::NaiveDateTime;
use crate::schema::pageviews;
/// This represents a page view pulled from the database, including the auto-generated fields
#[derive(Serialize, Deserialize, Queryable)]
pub struct PageView {
pub id: i64,
pub view_time: NaiveDateTime,
pub url: String,
pub user_agent: String,
pub referrer: String,
pub device_type: i8,
}
/// This represents a page view being inserted into the database, without the auto-generated fields
#[derive(Deserialize, Insertable)]
#[table_name = "pageviews"]
pub struct InsertablePageView {
pub url: String,
pub user_agent: String,
pub referrer: String,
pub device_type: i8,
}
|
cs |
# src/routes.rs 생성
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
|
use diesel::{self, prelude::*};
use rocket_contrib::json::Json;
use crate::models::{InsertablePageView, PageView};
use crate::schema;
use crate::DbConn;
#[get("/")]
pub fn index() -> &'static str {
"Application successfully started!"
}
#[post("/page_view", data = "<page_view>")]
pub fn create_page_view(
conn: DbConn,
page_view: Json<InsertablePageView>,
) -> Result<String, String> {
let inserted_rows = diesel::insert_into(schema::pageviews::table)
.values(&page_view.0)
.execute(&conn.0)
.map_err(|err| -> String {
println!("Error inserting row: {:?}", err);
"Error inserting row into database".into()
})?;
Ok(format!("Inserted {} row(s).", inserted_rows))
}
#[get("/page_view")]
pub fn list_page_views(conn: DbConn) -> Result<Json<Vec<PageView>>, String> {
use crate::schema::pageviews::dsl::*;
pageviews.load(&conn.0).map_err(|err| -> String {
println!("Error querying page views: {:?}", err);
"Error querying page views from the database".into()
}).map(Json)
}
|
cs |
# src/cors.rs 생성
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
|
use rocket::fairing::{Fairing, Info, Kind};
use rocket::{http::Method, http::Status, Request, Response};
pub struct CorsFairing;
impl Fairing for CorsFairing {
fn on_response(&self, request: &Request, response: &mut Response) {
// Add CORS headers to allow all origins to all outgoing requests
response.set_header(rocket::http::Header::new(
"Access-Control-Allow-Origin",
"*",
));
// Respond to all `OPTIONS` requests with a `204` (no content) status
if response.status() == Status::NotFound && request.method() == Method::Options {
response.set_status(Status::NoContent);
}
}
fn info(&self) -> Info {
Info {
name: "CORS Fairing",
kind: Kind::Response,
}
}
}
|
cs |
# 생성된 폴더 구조
# 로켓에게 알려주기
1
|
$ export ROCKET_DATABASES="{ rocket_app = { url = \"mysql://rocket:your_chosen_password@localhost/rocket_app\" } }"
|
cs |
# 실행
1
|
$ cargo run
|
cs |
- ERROR : error: failed to run custom build command for `pear_codegen v0.1.4`
Caused by:
process didn't exit successfully: `/home/user/rust-test/diesel_demo/target/debug/build/pear_codegen-6c0aed454a075096/build-script-build` (exit status: 101)
--- stderr
Error: Pear requires a 'dev' or 'nightly' version of rustc.
Installed version: 1.56.1 (2021-11-01)
Minimum required: 1.31.0-nightly (2018-10-05)
thread 'main' panicked at 'Aborting compilation due to incompatible compiler.', /home/user/.cargo/registry/src/github.com-1ecc6299db9ec823/pear_codegen-0.1.4/build.rs:24:13
note: run with `RUST_BACKTRACE=1` environment variable to display a backtrace
warning: build failed, waiting for other jobs to finish...
error: build failed
#
1
|
$ rustup default nightly
|
cs |
--- 결과 : info: syncing channel updates for 'nightly-x86_64-unknown-linux-gnu'
info: latest update on 2021-12-02, rust version 1.59.0-nightly (48a5999fc 2021-12-01)
info: downloading component 'cargo'
info: downloading component 'clippy'
info: downloading component 'rust-docs'
info: downloading component 'rust-std'
info: downloading component 'rustc'
info: downloading component 'rustfmt'
info: installing component 'cargo'
info: installing component 'clippy'
info: installing component 'rust-docs'
19.4 MiB / 19.4 MiB (100 %) 2.2 MiB/s in 10s ETA: 0s
info: installing component 'rust-std'
25.9 MiB / 25.9 MiB (100 %) 12.9 MiB/s in 2s ETA: 0s
info: installing component 'rustc'
54.5 MiB / 54.5 MiB (100 %) 15.0 MiB/s in 3s ETA: 0s
info: installing component 'rustfmt'
info: default toolchain set to 'nightly-x86_64-unknown-linux-gnu'
nightly-x86_64-unknown-linux-gnu installed - rustc 1.59.0-nightly (48a5999fc 2021-12-01)
# 다시 cargo 실행
1
|
$ cargo run
|
cs |
# 실행 결과
## Get : localhost:8000/
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
|
Blocking waiting for file lock on build directory
Compiling diesel_demo v0.1.0 (/home/user/rust-test/diesel_demo)
warning: Error finalizing incremental compilation session directory `/home/user/rust-test/diesel_demo/target/debug/incremental/diesel_demo-1nbj1jt6yr20z/s-g4s0295q1h-1pzh0gs-working`: Permission denied (os error 13)
warning: `diesel_demo` (bin "diesel_demo") generated 1 warning
Finished dev [unoptimized + debuginfo] target(s) in 2m 14s
Running `target/debug/diesel_demo`
🔧 Configured for development.
=> address: localhost
=> port: 8000
=> log: normal
=> workers: 12
=> secret key: generated
=> limits: forms = 32KiB
=> keep-alive: 5s
=> read timeout: 5s
=> write timeout: 5s
=> tls: disabled
=> [extra] databases: { rocket_app = { url = "mysql://rocket:your_chosen_password@localhost/rocket_app" } }
🛰 Mounting /:
=> GET / (index)
=> POST /page_view (create_page_view)
=> GET /page_view (list_page_views)
📡 Fairings:
=> 1 response: CORS Fairing
🚀 Rocket has launched from http://localhost:8000
|
cs |
## Post : localhost:8000/page_view
## Get: localhost:8000/page_view
-------------------------------------------------------restart-------------------------------------------------------
$ export DATABASE_URL="mysql://rocket:your_chosen_password@localhost/rocket_app"
$ export ROCKET_DATABASES="{ rocket_app = { url = \"mysql://rocket:your_chosen_password@localhost/rocket_app\" } }"
cs
-------------------------------------------------------restart-------------------------------------------------------
-------------------------------------------------------restart-------------------------------------------------------
-------------------------------------------------------restart-------------------------------------------------------