Merge pull request #6 from 8thlight/VDB-337-Dockerize-vDB

VDB-337 Dockerize vDB
This commit is contained in:
Edvard Hübinette 2019-02-11 12:37:56 +01:00 committed by GitHub
commit f73623789e
14 changed files with 176 additions and 45 deletions

16
.dockerignore Normal file
View File

@ -0,0 +1,16 @@
.git
.travis.yml
.idea
bin
.gitignore
integration_test
LICENSE
postgraphile
.private_blockchain_password
README.md
scripts
Supfile
test_config
.travis.yml
vulcanizedb.log
Dockerfile

24
Dockerfile Normal file
View File

@ -0,0 +1,24 @@
FROM golang:alpine as builder
RUN apk --update --no-cache add make git g++
# Build statically linked vDB binary (wonky path because of Dep)
RUN mkdir -p /go/src/github.com/vulcanize/vulcanizedb
ADD . /go/src/github.com/vulcanize/vulcanizedb
WORKDIR /go/src/github.com/vulcanize/vulcanizedb
RUN GCO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' .
# Build migration tool
RUN go get -u -d github.com/pressly/goose/cmd/goose
WORKDIR /go/src/github.com/pressly/goose/cmd/goose
RUN GCO_ENABLED=0 GOOS=linux go build -a -installsuffix cgo -ldflags '-extldflags "-static"' -tags='no_mysql no_sqlite' -o goose
# Second stage
FROM alpine
COPY --from=builder /go/src/github.com/vulcanize/vulcanizedb/vulcanizedb /app/vulcanizedb
COPY --from=builder /go/src/github.com/vulcanize/vulcanizedb/environments/staging.toml /app/environments/
COPY --from=builder /go/src/github.com/vulcanize/vulcanizedb/dockerfiles/startup_script.sh /app/
COPY --from=builder /go/src/github.com/vulcanize/vulcanizedb/db/migrations/* /app/
COPY --from=builder /go/src/github.com/pressly/goose/cmd/goose/goose /app/goose
WORKDIR /app
CMD ["./startup_script.sh"]

View File

@ -19,6 +19,7 @@ package cmd
import ( import (
"fmt" "fmt"
"os" "os"
"strings"
"time" "time"
"github.com/ethereum/go-ethereum/ethclient" "github.com/ethereum/go-ethereum/ethclient"
@ -78,6 +79,9 @@ func database(cmd *cobra.Command, args []string) {
func init() { func init() {
cobra.OnInitialize(initConfig) cobra.OnInitialize(initConfig)
// When searching for env variables, replace dots in config keys with underscores
viper.SetEnvKeyReplacer(strings.NewReplacer(".", "_"))
viper.AutomaticEnv()
rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file location") rootCmd.PersistentFlags().StringVar(&cfgFile, "config", "", "config file location")
rootCmd.PersistentFlags().String("database-name", "vulcanize_public", "database name") rootCmd.PersistentFlags().String("database-name", "vulcanize_public", "database name")

32
dockerfiles/README.md Normal file
View File

@ -0,0 +1,32 @@
S
`Dockerfile` will build an alpine image containing:
- vDB as a binary with runtime deps statically linked: `/app/vulcanizedb`
- The migration tool goose: `/app/goose`
- Two services for running `lightSync` and `continuousLogSync`, started with the default configuration `environments/staging.toml`.
By default, vDB is configured towards the Kovan deploy. The configuration values can be overridden using environment variables, using the same hierarchical naming pattern but in CAPS and using underscores. For example, the contract address for the `Pit` can be set with the variable `CONTRACT_ADDRESS_PIT="0x123..."`.
## To use the container:
1. Setup a postgres database with superuser `vulcanize`
2. Set the env variables `DATABASE_NAME`, `DATABASE_HOSTNAME`,
`DATABASE_PORT`, `DATABASE_USER` & `DATABASE_PASSWORD`
3. Run the DB migrations:
* `./goose postgres "postgresql://$(DATABASE_USER):$(DATABASE_PASSWORD)@$(DATABASE_HOSTNAME):$(DATABASE_PORT)/$(DATABASE_NAME)?sslmode=disable"
e`
4. Set `CLIENT_IPCPATH` to a node endpoint
5. Set the contract variables:
* `CONTRACT_ADDRESS_[CONTRACT NAME]=0x123...`
* `CONTRACT_ABI_[CONTRACT NAME]="ABI STRING"`
* `CONTRACT_DEPLOYMENT-BLOCK_[CONTRACT NAME]=0` (doesn't really matter on a short chain, just avoids long unnecessary searching)
6. Start the `lightSync` and `continuousLogSync` services:
* `./vulcanizedb lightSync --config environments/staging.toml`
* `./vulcanizedb continuousLogSync --config environments/staging.toml`
### Automated
The steps above have been rolled into a script: `/app/startup_script.sh`, which just assumes the DB env variables have been set, and defaults the rest to Kovan according to `environments/staging.toml`. This can be called with something like:
`docker run -d -e DATABASE_NAME=vulcanize_public -e DATABASE_HOSTNAME=localhost -e DATABASE_PORT=5432 -e DATABASE_USER=vulcanize -e DATABASE_PASSWORD=vulcanize m0ar/images:vDB`
### Logging
When running, vDB services log to `/app/vulcanizedb.log`.

29
dockerfiles/startup_script.sh Executable file
View File

@ -0,0 +1,29 @@
#!/bin/sh
# Runs the migrations and starts the lightSync and continuousLogSync services
# Exit if the variable tests fail
set -e
# Check the database variables are set
test $DATABASE_NAME
test $DATABASE_HOSTNAME
test $DATABASE_PORT
test $DATABASE_USER
test $DATABASE_PASSWORD
# Construct the connection string for postgres
CONNECT_STRING=postgresql://$DATABASE_USER:$DATABASE_PASSWORD@$DATABASE_HOSTNAME:$DATABASE_PORT/$DATABASE_NAME?sslmode=disable
echo "Connecting with: $CONNECT_STRING"
set +e
# Run the DB migrations
./goose postgres "$CONNECT_STRING" up
if [ $? -eq 0 ]; then
# Fire up the services
./vulcanizedb lightSync --config environments/staging.toml &
./vulcanizedb continuousLogSync --config environments/staging.toml &
else
echo "Could not run migrations. Are the database details correct?"
fi
wait

View File

@ -1,8 +1,4 @@
[database] [database]
name = "vulcanize_public"
hostname = "localhost"
user = "vulcanize"
password = "vulcanize"
port = 5432 port = 5432
[client] [client]

View File

@ -18,6 +18,7 @@ package postgres
import ( import (
"errors" "errors"
"github.com/sirupsen/logrus"
"github.com/jmoiron/sqlx" "github.com/jmoiron/sqlx"
_ "github.com/lib/pq" //postgres driver _ "github.com/lib/pq" //postgres driver
@ -40,6 +41,7 @@ var (
func NewDB(databaseConfig config.Database, node core.Node) (*DB, error) { func NewDB(databaseConfig config.Database, node core.Node) (*DB, error) {
connectString := config.DbConnectionString(databaseConfig) connectString := config.DbConnectionString(databaseConfig)
logrus.Info("Using connection string: ", connectString)
db, err := sqlx.Connect("postgres", connectString) db, err := sqlx.Connect("postgres", connectString)
if err != nil { if err != nil {
return &DB{}, ErrDBConnectionFailed return &DB{}, ErrDBConnectionFailed

View File

@ -6,23 +6,7 @@ import (
"github.com/spf13/viper" "github.com/spf13/viper"
) )
var initialized = false
func initConfig() {
if initialized {
return
}
if err := viper.ReadInConfig(); err == nil {
fmt.Printf("Using config file: %s\n\n", viper.ConfigFileUsed())
} else {
panic(fmt.Sprintf("Could not find environment file: %v", err))
}
initialized = true
}
func getEnvironmentString(key string) string { func getEnvironmentString(key string) string {
initConfig()
value := viper.GetString(key) value := viper.GetString(key)
if value == "" { if value == "" {
panic(fmt.Sprintf("No environment configuration variable set for key: \"%v\"", key)) panic(fmt.Sprintf("No environment configuration variable set for key: \"%v\"", key))
@ -30,11 +14,11 @@ func getEnvironmentString(key string) string {
return value return value
} }
// Returns an int from the environment, defaulting to 0 if it does not exist
func getEnvironmentInt64(key string) int64 { func getEnvironmentInt64(key string) int64 {
initConfig()
value := viper.GetInt64(key) value := viper.GetInt64(key)
if value == -1 { if value == -1 {
panic(fmt.Sprintf("No environment configuration variable set for key: \"%v\"", key)) return 0
} }
return value return value
} }

View File

@ -0,0 +1,7 @@
package-lock.json
yarn.lock
node_modules
Dockerfile
README.md
spec
.dockerignore

9
postgraphile/Dockerfile Normal file
View File

@ -0,0 +1,9 @@
FROM mhart/alpine-node:10
RUN apk --update --no-cache add make g++ python findutils postgresql-dev
WORKDIR /app
COPY . /app
run yarn install
RUN ["./node_modules/typescript/bin/tsc"]
EXPOSE 3000
CMD ["node", "/app/build/dist/index.js"]

View File

@ -2,6 +2,18 @@
This application utilizes Postgraphile to expose GraphQL endpoints for exposure of the varied data that VulcanizeDB tracks. This application utilizes Postgraphile to expose GraphQL endpoints for exposure of the varied data that VulcanizeDB tracks.
## Docker use
_Note: currently this image is ~500MB large (unpacked)_
Build the docker image in this directory. Start the `GraphiQL` frontend by:
* Setting the env variables for the database connection: `DATABASE_HOST`,
`DATABASE_NAME`, `DATABASE_USER`, `DATABASE_PASSWORD` (and optionally
`DATABASE_PORT` if running on non-standard port).
* The specified user needs to be `superuser` on the vulcanizeDB database
* Run the container (ex. `docker run -e DATABASE_HOST=localhost -e DATABASE_NAME=vulcanize_public -e DATABASE_USER=vulcanize -e DATABASE_PASSWORD=vulcanize -d m0ar/images:postgraphile-alpine`)
* GraphiQL is available at `:3000/graphiql`
## Building ## Building
*This application assumes the use of the [Yarn package manager](https://yarnpkg.com/en/). The use of npm may produce unexpected results.* *This application assumes the use of the [Yarn package manager](https://yarnpkg.com/en/). The use of npm may produce unexpected results.*

View File

@ -22,22 +22,22 @@
"homepage": "https://github.com/vulcanize/vulcanizedb", "homepage": "https://github.com/vulcanize/vulcanizedb",
"dependencies": { "dependencies": {
"express-session": "1.15.6", "express-session": "1.15.6",
"graphql-subscriptions": "0.5.8",
"lodash": "4.17.10", "lodash": "4.17.10",
"passport": "0.4.0", "passport": "0.4.0",
"pg": "6.4.2",
"pg-native": "3.0.0", "pg-native": "3.0.0",
"postgraphile": "4.0.0-rc.4", "postgraphile": "4.0.0-rc.4",
"graphql-subscriptions": "0.5.8",
"subscriptions-transport-ws": "0.9.14", "subscriptions-transport-ws": "0.9.14",
"toml": "2.3.3", "toml": "2.3.3"
"pg": "6.4.2"
}, },
"devDependencies": { "devDependencies": {
"@types/graphql": "^0.13.4",
"@types/express": "4.16.0", "@types/express": "4.16.0",
"@types/express-session": "1.15.10", "@types/express-session": "1.15.10",
"@types/graphql": "^0.13.4",
"@types/jasmine": "2.8.8", "@types/jasmine": "2.8.8",
"@types/lodash": "4.14.116", "@types/lodash": "4.14.116",
"@types/node": "10.9.3", "@types/node": "^10.12.21",
"@types/passport": "0.4.6", "@types/passport": "0.4.6",
"awesome-typescript-loader": "5.2.0", "awesome-typescript-loader": "5.2.0",
"jasmine": "3.2.0", "jasmine": "3.2.0",

View File

@ -6,34 +6,49 @@ export const MISSING_PATH_MESSAGE = `No path to config toml file provided, `
+ `please check the value of ${CONFIG_PATH_KEY} in your environment`; + `please check the value of ${CONFIG_PATH_KEY} in your environment`;
export const MISSING_HOST_MESSAGE = 'No database host provided in config toml'; export const MISSING_HOST_MESSAGE = 'No database host provided in config toml';
export const MISSING_DATABASE_MESSAGE = 'No database name provided in config ' export const MISSING_USER_MESSAGE = 'No database user & password provided in config toml';
+ 'toml'; export const MISSING_DATABASE_MESSAGE = 'No database name provided in config toml';
export function parseConfig( export function parseConfig(
readCallback: ReadFileSyncCallback, readCallback: ReadFileSyncCallback,
tomlParseCallback: TomlParseCallback, tomlParseCallback: TomlParseCallback,
configPath?: string configPath?: string
): DatabaseConfig { ): DatabaseConfig {
if (!configPath || configPath.length < 1) { let host = '';
throw new Error(MISSING_PATH_MESSAGE); let port = '';
let database = '';
let user = '';
let password = '';
if (configPath) {
const tomlContents = readCallback(`${configPath}`).toString();
const parsedToml = tomlParseCallback(tomlContents);
host = parsedToml['database']['hostname'];
port = parsedToml['database']['port'];
database = parsedToml['database']['name'];
user = parsedToml['database']['user'];
password = parsedToml['database']['password'];
} }
const tomlContents = readCallback(`${configPath}`).toString(); // Overwrite config values with env. vars if such are set
const parsedToml = tomlParseCallback(tomlContents); host = process.env.DATABASE_HOST || host;
port = process.env.DATABASE_PORT || port;
database = process.env.DATABASE_NAME || database;
user = process.env.DATABASE_USER || user;
password = process.env.DATABASE_PASSWORD || password;
const host = parsedToml['database']['hostname']; if (!host) {
const port = parsedToml['database']['port'];
const database = parsedToml['database']['name'];
const user = parsedToml['database']['user'] || '';
const password = parsedToml['database']['password'] || '';
if (!host || host.length < 1) {
throw new Error(MISSING_HOST_MESSAGE); throw new Error(MISSING_HOST_MESSAGE);
} }
if (!database || database.length < 1) { if (!database) {
throw new Error(MISSING_DATABASE_MESSAGE); throw new Error(MISSING_DATABASE_MESSAGE);
} }
if (!user || !password) {
throw new Error(MISSING_USER_MESSAGE);
}
return { host: `postgres://${user}:${password}@${host}:${port}`, database }; return { host: `postgres://${user}:${password}@${host}:${port}`, database };
} }

View File

@ -108,9 +108,10 @@
version "10.9.4" version "10.9.4"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.4.tgz#0f4cb2dc7c1de6096055357f70179043c33e9897" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.4.tgz#0f4cb2dc7c1de6096055357f70179043c33e9897"
"@types/node@10.9.3": "@types/node@^10.12.21":
version "10.9.3" version "10.12.21"
resolved "https://registry.yarnpkg.com/@types/node/-/node-10.9.3.tgz#85f288502503ade0b3bfc049fe1777b05d0327d5" resolved "https://registry.yarnpkg.com/@types/node/-/node-10.12.21.tgz#7e8a0c34cf29f4e17a36e9bd0ea72d45ba03908e"
integrity sha512-CBgLNk4o3XMnqMc0rhb6lc77IwShMEglz05deDcn2lQxyXEZivfwgYJu7SMha9V5XcrP6qZuevTHV/QrN2vjKQ==
"@types/passport@0.4.6": "@types/passport@0.4.6":
version "0.4.6" version "0.4.6"