• 14-Deployment On Heroku
    • Heroku
    • 创建Heroku账户
    • 安装Heroku命令行工具
    • 创建Heroku应用
    • 创建 Heroku 数据库
    • 初始化数据
      • 针对Heroku做些代码优化
    • 数据init
  • 部署 Heroku
    • 设置Config Vars
    • Procfile
    • Go dep
    • Push heroku
  • Links

    14-Deployment On Heroku

    在本章,我会将应用部署到Heroku云平台

    许多云托管提供商提供了一个应用程序可以运行的托管平台。 你只需提供部署到这些平台上的实际应用程序,因为硬件,操作系统,脚本语言解释器,数据库等都由该服务管理。 这种服务称为平台即服务(PaaS)。

    Heroku,这是一种流行的云托管服务,对Python、Go、Nodejs等应用程序支持都很好,关键还免费,而且默认支持HTTPS

    本章的GitHub链接为: Source, Diff, Zip

    Heroku

    Heroku 是首批PaaS平台之一。 它以Ruby的应用程序的托管服务开始,随后逐渐发展到支持诸多其他语言,如Java,Node.js,Python 还有Go。

    在 Heroku 中部署Web应用程序主要是通过git版本控制工具完成的,因此你必须将应用程序放在git代码库中。 在通过git将应用程序上传到 Heroku 的服务器之后,你的工作基本就完成了,只需等待几秒钟,应用程序就会上线。 整个操作流程就是这么简单。

    Heroku提供不同的服务级别,允许你自主选择为应用程序提供多少计算能力和运行时间,随着用户群的增长,你需要购买更多的“dynos”计算单元。

    创建Heroku账户

    在部署应用到 Heroku 之前,你需要拥有一个帐户。 所以请访问 heroku.com 并创建一个免费账户。 一旦注册成功并登录到 Heroku,你将可以访问一个dashboard,其中列出了你的所有应用程序。

    安装Heroku命令行工具

    我们可以在 Heroku 的 dashboard 上完成所有操作,不过还有个更简便的方法,就是安装Heroku cli 工具

    1. $ brew install heroku

    然后我们可以通过命令行来登陆 Heroku

    1. $ heroku login

    创建Heroku应用

    要用Heroku注册一个新应用,需要在应用程序根目录下使用apps:create子命令,并将应用程序名称作为唯一参数传递:

    1. $ heroku apps:create go-mega
    2. Creating go-mega... done
    3. https://go-mega.herokuapp.com/ | https://git.heroku.com/go-mega.git

    创建 Heroku 数据库

    Heroku 只有Postgres数据库是免费的,Mysql是收费的,所以我们还是创建 Postgres 数据库

    1. $ heroku addons:add heroku-postgresql:hobby-dev
    2. Creating heroku-postgresql:hobby-dev on go-mega... free
    3. Database has been created and is available
    4. ! This database is empty. If upgrading, you can transfer
    5. ! data from another database with pg:copy
    6. Created postgresql-angular-82467 as DATABASE_URL
    7. Use heroku addons:docs heroku-postgresql to view documentation

    初始化数据

    1. $ heroku config
    2. === go-mega Config Vars
    3. DATABASE_URL: ******************

    针对Heroku做些代码优化

    由于Heroku采用的是 Configvar 的设置环境变量的方式,而且我们把 config.yml git ignore了,所以加入 os.Getenv 的方式去获取Configvar的环境变量,包括 DBTYPE, EMAIL相关,以及Heroku postgres 提供的 DATABASE_URL

    config/g.go

    1. package config
    2. import (
    3. "fmt"
    4. "log"
    5. "os"
    6. "strconv"
    7. "github.com/spf13/viper"
    8. )
    9. func init() {
    10. projectName := "go-mega"
    11. dbType := GetDBType()
    12. log.Println("OS DBTYPE:", dbType)
    13. if IsHeroku() {
    14. log.Println("Get Env from os.env")
    15. } else {
    16. log.Println("Init viper")
    17. getConfig(projectName)
    18. }
    19. }
    20. func getConfig(projectName string) {
    21. viper.SetConfigName("config") // name of config file (without extension)
    22. viper.AddConfigPath(".") // optionally look for config in the working directory
    23. viper.AddConfigPath(fmt.Sprintf("$HOME/.%s", projectName)) // call multiple times to add many search paths
    24. viper.AddConfigPath(fmt.Sprintf("/data/docker/config/%s", projectName)) // path to look for the config file in
    25. err := viper.ReadInConfig() // Find and read the config file
    26. if err != nil { // Handle errors reading the config file
    27. panic(fmt.Errorf("Fatal error config file: %s", err))
    28. }
    29. }
    30. // GetMysqlConnectingString func
    31. func GetMysqlConnectingString() string {
    32. usr := viper.GetString("mysql.user")
    33. pwd := viper.GetString("mysql.password")
    34. host := viper.GetString("mysql.host")
    35. db := viper.GetString("mysql.db")
    36. charset := viper.GetString("mysql.charset")
    37. return fmt.Sprintf("%s:%s@tcp(%s:3306)/%s?charset=%s&parseTime=true&loc=Local", usr, pwd, host, db, charset)
    38. }
    39. // GetHerokuConnectingString func
    40. func GetHerokuConnectingString() string {
    41. return os.Getenv("DATABASE_URL")
    42. }
    43. // GetSMTPConfig func
    44. func GetSMTPConfig() (server string, port int, user, pwd string) {
    45. if IsHeroku() {
    46. server = os.Getenv("MAIL_SMTP")
    47. port, _ = strconv.Atoi(os.Getenv("MAIL_SMTP_PORT"))
    48. user = os.Getenv("MAIL_USER")
    49. pwd = os.Getenv("MAIL_PASSWORD")
    50. return
    51. }
    52. server = viper.GetString("mail.smtp")
    53. port = viper.GetInt("mail.smtp-port")
    54. user = viper.GetString("mail.user")
    55. pwd = viper.GetString("mail.password")
    56. return
    57. }
    58. // GetServerURL func
    59. func GetServerURL() (url string) {
    60. if IsHeroku() {
    61. url = os.Getenv("SERVER_URL")
    62. return
    63. }
    64. url = viper.GetString("server.url")
    65. return
    66. }
    67. // GetDBType func
    68. func GetDBType() string {
    69. dbtype := os.Getenv("DBTYPE")
    70. return dbtype
    71. }
    72. // IsHeroku func
    73. func IsHeroku() bool {
    74. return GetDBType() == "heroku"
    75. }

    修改ConnectToDB函数,支持postgres的数据库形式

    model/g.go

    1. ...
    2. // ConnectToDB func
    3. func ConnectToDB() *gorm.DB {
    4. if config.IsHeroku() {
    5. return ConnectToDBByDBType("postgres", config.GetHerokuConnectingString())
    6. }
    7. return ConnectToDBByDBType("mysql", config.GetMysqlConnectingString())
    8. }
    9. // ConnectToDBByDBType func
    10. func ConnectToDBByDBType(dbtype, connectingStr string) *gorm.DB {
    11. log.Println("DB Type:", dbtype, "\nConnet to db...")
    12. db, err := gorm.Open(dbtype, connectingStr)
    13. if err != nil {
    14. panic("Failed to connect database")
    15. }
    16. db.SingularTable(true)
    17. return db
    18. }

    cmd/db_init/main.go

    1. ...
    2. _ "github.com/jinzhu/gorm/dialects/postgres"
    3. ...

    main.go 与数据初始化无关,不过最后部署还是要做响应的调整

    main.go

    1. ...
    2. _ "github.com/jinzhu/gorm/dialects/postgres"
    3. ...
    4. port := os.Getenv("PORT")
    5. log.Println("Running on port: ", port)
    6. http.ListenAndServe(":"+port, context.ClearHandler(http.DefaultServeMux))

    数据init

    1. $ export DATABASE_URL=postgres://xxxxxxx
    2. $ export DBTYPE=heroku
    3. $ go run cmd/db_init/main.go

    然后我们会发现数据初始化就完成了,让我们确认下

    1. # 安装 psql
    2. $ brew install postgresql
    3. $ heroku pg:psql
    4. $ DATABASE=> select * from post;
    5. id | user_id | body | timestamp
    6. ----+---------+---------------------------------+-------------------------------
    7. 1 | 1 | Beautiful day in Portland! | 2018-10-23 07:13:30.664214+00
    8. 2 | 2 | The Avengers movie was so cool! | 2018-10-23 07:13:36.118051+00
    9. 3 | 2 | Sun shine is beautiful | 2018-10-23 07:13:38.502164+00
    10. # 退出
    11. $ DATABASE=> \q

    本小节 Diff

    部署 Heroku

    设置Config Vars

    可以访问 https://dashboard.heroku.com/apps/go-mega/settings dashboard 的Config Vars 进行设置

    也可以通过heroku cli

    1. $ heroku config:set DBTYPE=heroku
    2. # 设置root url
    3. $ heroku config:set SERVER_URL=https://go-mega.herokuapp.com
    4. # 设置mail
    5. $ heroku config:set MAIL_SMTP=smtp.zoho.com
    6. $ heroku config:set MAIL_SMTP_PORT=587
    7. $ heroku config:set MAIL_USER=your_username
    8. $ heroku config:set MAIL_PASSWORD=your_password
    9. # 查看config
    10. $ heroku config

    Procfile

    Procfile

    1. web: go-mega-code

    现在我们可以通过 heroku local来在本地查看应用

    1. $ heroku local

    Go dep

    目前我们的代码和配置都已经完成了,不过部署 heroku 还需要我们提供依赖,我们这里使用 Godep

    可以参照Go Dependencies via Godep

    1. $ go get -u github.com/tools/godep
    2. $ godep save ./...
    3. # 结果可以看见多了两个文件夹: vendor/ 和 Godeps/

    Push heroku

    1. # Remote add heroku
    2. $ heroku git:remote -a go-mega
    3. set git remote heroku to https://git.heroku.com/go-mega.git
    4. $ git remote -v
    5. heroku https://git.heroku.com/go-mega.git (fetch)
    6. heroku https://git.heroku.com/go-mega.git (push)
    7. origin git@github.com:bonfy/go-mega-code.git (fetch)
    8. origin git@github.com:bonfy/go-mega-code.git (push)
    9. # push branch to heroku master
    10. $ git push heroku 14-Deployment-On-Heroku:master

    现在访问 https://go-mega.herokuapp.com/ 就能看见 Demo 了

    Notice: Heroku 绑定域名要收费,所以这里就不绑定域名了

    14-01

    本小节 Diff

    • 目录
    • 上一节: 13-Javascript-Magic
    • 下一节: 15-Deployment-On-Linux