diff --git a/docker/README.md b/docker/README.md new file mode 100644 index 0000000..27fae1e --- /dev/null +++ b/docker/README.md @@ -0,0 +1,45 @@ +# dockerized libbitcoin + +## bitcoin server + +A linux image can be generated with the script below. Currently, this is only tested against the `version3` branch with or without the `v3.8.0` tag. + +### linux + +The following outlines usage of the `bs-linux` artifacts. +It is of note that the generated image includes a `startup.sh` script which conditionally initializes the blockchain database if not found. + +#### Initialization + +The following populates a provided image build directory: + +``` +./bs-linux.sh --build-dir= --source= --init-only +``` + +The reader then needs to edit the generated `bs-linux.env` to provide storage paths for volume mounting. +Additionally, the `STORAGE_BITCOIN_CONF` path should contain a `bs.cfg` containing a `directory` field for `[database]` which has some depth from the volume mount point, due to `bs` implementation attempts to create the blockchain containing directory. + +#### Image construction + +The image construction can then continue via an other invocatio with the following parameters: + +``` +./bs-linux.sh --build-dir= --source= --build-only +``` + +#### Container management + +The file `bs-linux.yml` is provided in the image build directory in order to allow `docker compose` to control container construction and destruction. +The reader is free to translate this file and the `bs-linux.env` into `docker` commands derived from the declative `yaml`. +The timeout parameter should be noted: `bs` has been known to take an extended period of time to write memory to disk and `SIGKILL` will likely corrupt the database. + +##### Instantiating container +``` +docker compose --env-file bs-linux.env --file bs-linux.yml up -t 3600 -d +``` + +##### Terminating container +``` +docker compose --env-file bs-linux.env --file bs-linux.yml down -t 3600 -v +``` diff --git a/docker/bs-linux-startup.sh b/docker/bs-linux-startup.sh new file mode 100755 index 0000000..2c0603b --- /dev/null +++ b/docker/bs-linux-startup.sh @@ -0,0 +1,72 @@ +#!/bin/bash + +set -ex + +SCRIPT_IDENTITY=$(basename "$0") + +display_message() +{ + printf "%s\n" "$@" +} + +display_error() +{ + >&2 printf "%s\n" "$@" +} + +display_help() +{ + display_message "Usage: ./${SCRIPT_IDENTITY} [OPTION]..." + display_message "Docker instance startup script." + display_message "Script options:" + display_message " --unconditional-init Force database reset." + display_message "" +} + +fatal_error() +{ + display_error "$@" + display_error "" + display_help + exit 1 +} + +dispatch_help() +{ + if [[ ${DISPLAY_HELP} ]]; then + display_help + exit 0 + fi +} + +parse_command_line_options() +{ + for OPTION in "$@"; do + case ${OPTION} in + # Specific + (--unconditional-init) INIT_UNCONDITIONAL="yes";; + + # Standard + (--help) DISPLAY_HELP="yes";; + esac + done +} + +initialize() +{ + if [[ -z "$( ls -A blockchain )" ]]; then + ./bs -c conf/bs.cfg --initchain + elif [[ $INIT_UNCONDITIONAL ]]; then + ./bs -c conf/bs.cfg --initchain + fi +} + +execute_service() +{ + ./bs -c conf/bs.cfg +} + +parse_command_line_options "$@" +dispatch_help +initialize +execute_service diff --git a/docker/bs.Dockerfile b/docker/bs-linux.Dockerfile similarity index 75% rename from docker/bs.Dockerfile rename to docker/bs-linux.Dockerfile index d9390c5..5fc323e 100644 --- a/docker/bs.Dockerfile +++ b/docker/bs-linux.Dockerfile @@ -30,8 +30,13 @@ RUN /build/developer_setup.sh \ FROM alpine:latest AS runtime -COPY --from=build /build/prefix/bin/bs /bitcoin +ENV BUILD_DEPS="build-base linux-headers gcc make autoconf automake libtool pkgconf git wget bash" + +RUN apk update && \ + apk add --update ${BUILD_DEPS} +COPY --from=build /build/prefix/bin/bs /bitcoin/bs +COPY bs-linux-startup.sh /bitcoin/startup.sh # Bitcoin P2P EXPOSE 8333/tcp EXPOSE 8333/udp @@ -52,6 +57,6 @@ EXPOSE 9093/tcp EXPOSE 9084/tcp EXPOSE 9094/tcp -VOLUME ["/bitcoin/data", "/bitcoin/conf"] -ENTRYPOINT ["/bitcoin/bs"] -CMD ["-c", "/bitcoin/conf/bs.cfg", "-i", "/bitcoin/data"] +WORKDIR /bitcoin +VOLUME ["/bitcoin/blockchain", "/bitcoin/conf"] +CMD [ "/bitcoin/startup.sh" ] diff --git a/docker/bs-linux.env.in b/docker/bs-linux.env.in new file mode 100644 index 0000000..3501995 --- /dev/null +++ b/docker/bs-linux.env.in @@ -0,0 +1,4 @@ +COMPOSE_PROJECT_NAME=bitcoin +VERSION_BITCOIN_SERVER=%version% +STORAGE_BITCOIN_CONF=%SET_ME% +STORAGE_BITCOIN_DATA=%SET_ME% diff --git a/docker/bs-linux.sh b/docker/bs-linux.sh new file mode 100755 index 0000000..f8a694c --- /dev/null +++ b/docker/bs-linux.sh @@ -0,0 +1,202 @@ +#!/bin/bash + +set -e + +SCRIPT_IDENTITY=$(basename "$0") + +display_message() +{ + printf "%s\n" "$@" +} + +display_error() +{ + >&2 printf "%s\n" "$@" +} + +fatal_error() +{ + display_error "$@" + display_error "" + display_help + exit 1 +} + +display_help() +{ + display_message "Usage: ./${SCRIPT_IDENTITY} [OPTION]..." + display_message "Manage the generation of a docker image of bitcoin-server (bs)." + display_message "Script options:" + display_message " --build-dir= Directory for building docker image" + display_message " --source= xml file for gsl generation." + display_message " --tag= git tag to utilize with source instructions." + display_message " --build-only docker build without reinitializing build-dir." + display_message " --init-only Initialize build-dir for docker image building." + display_message "" +} + +parse_command_line_options() +{ + for OPTION in "$@"; do + case ${OPTION} in + # Specific + (--build-dir=*) DIR_BUILD="${OPTION#*=}";; + (--source=*) SOURCE="${OPTION#*=}";; + (--tag=*) TAG="${OPTION#*=}";; + (--build-only) BUILD_ONLY="yes";; + (--init-only) INIT_ONLY="yes";; + # Standard + (--help) DISPLAY_HELP="yes";; + esac + done +} + +display_state() +{ + display_message "Parameters:" + display_message " DIR_BUILD : ${DIR_BUILD}" + display_message " SOURCE : ${SOURCE}" + display_message " TAG : ${TAG}" + display_message " BUILD_ONLY : ${BUILD_ONLY}" + display_message " INIT_ONLY : ${INIT_ONLY}" + display_message "Deduced:" + display_message " DIR_BUILD_PROJ : ${DIR_BUILD_PROJ}" + display_message " VERSION : ${VERSION}" +} + +dispatch_help() +{ + if [[ ${DISPLAY_HELP} ]]; then + display_help + exit 0 + fi +} + +validate_parameterization() +{ + if [ -z ${DIR_BUILD} ]; then + fatal_error " --build-dir= required." + fi + + if [ -z ${SOURCE} ]; then + fatal_error " --source= required." + fi +} + +initialize_environment() +{ + # https://stackoverflow.com/questions/59895/how-do-i-get-the-directory-where-a-bash-script-is-located-from-within-the-script + local DIR_SCRIPT=$(cd -- "$( dirname -- "${BASH_SOURCE[0]}" )" &> /dev/null && pwd ) + + # initialize DIR_BUILD_PROJ + pushd "${DIR_SCRIPT}/.." + DIR_BUILD_PROJ="$(pwd)" + popd + + if [ -f ${DIR_BUILD_PROJ}/${SOURCE} ]; then + SOURCE_VERSION=$(grep ' must be a valid filename in the expected path." + fi + + # rationalize intended version + if [ -z ${TAG} ]; then + VERSION="${SOURCE_VERSION}" + else + if [[ "${TAG}" =~ ^v.* ]]; then + VERSION=$(echo "${TAG}" | sed -n s/v//p) + else + VERSION="${TAG}" + fi + fi +} + +generate_instructions() +{ + display_message "Generate developer_setup.sh..." + + # create developer_setup.sh + pushd ${DIR_BUILD_PROJ} + eval gsl -q -script:templates/gsl.developer_setup.sh ${SOURCE} + chmod +x output/libbitcoin-*/developer_setup.sh + popd +} + +emit_compose() +{ + cp ${DIR_BUILD_PROJ}/docker/bs-linux.yml ${DIR_BUILD}/bs-linux.yml + sed "s/%version%/${VERSION}/" \ + ${DIR_BUILD_PROJ}/docker/bs-linux.env.in > ${DIR_BUILD}/bs-linux.env +} + +clean_build_directory() +{ + # cleanup build directory + if [ -d ${DIR_BUILD} ]; then + display_message "Directory '${DIR_BUILD}' found, emptying..." + pushd ${DIR_BUILD} + rm -rf bs-linux-startup.sh developer_setup.sh src/ + mkdir -p "${DIR_BUILD}/src" + popd + else + display_message "Directory '${DIR_BUILD}' not found, building..." + mkdir -p "${DIR_BUILD}/src" + fi +} + +initialize_build_directory() +{ + if [[ $BUILD_ONLY ]]; then + return 0 + fi + + clean_build_directory + generate_instructions + + display_message "Initialize build directory contents..." + cp ${DIR_BUILD_PROJ}/output/libbitcoin-server/developer_setup.sh ${DIR_BUILD} + cp ${DIR_BUILD_PROJ}/docker/bs-linux-startup.sh ${DIR_BUILD} + + pushd ${DIR_BUILD} + + local BUILD_TAG_PARAM="" + if [ -z ${TAG} ]; then + BUILD_TAG_PARAM="" + else + BUILD_TAG_PARAM="--build-tag=${TAG}" + fi + + ./developer_setup.sh ${BUILD_TAG_PARAM} \ + --build-sync-only \ + --build-src-dir="${DIR_BUILD}/src" \ + --build-target=all + + popd + + emit_compose +} + +dockerize() +{ + if [[ $INIT_ONLY ]]; then + return 0 + fi + + pushd ${DIR_BUILD} + display_message "----------------------------------------------------------------------" + display_message "docker build : libbitcoin/bitcoin-server:${VERSION}" + display_message "----------------------------------------------------------------------" + docker build -t libbitcoin/bitcoin-server:${VERSION} -f ${DIR_BUILD_PROJ}/docker/bs-linux.Dockerfile . + popd +} + +parse_command_line_options "$@" +dispatch_help +validate_parameterization +initialize_environment +initialize_build_directory +display_state +dockerize diff --git a/docker/bs-linux.yml b/docker/bs-linux.yml new file mode 100644 index 0000000..b1ab8aa --- /dev/null +++ b/docker/bs-linux.yml @@ -0,0 +1,25 @@ +networks: + net: + +services: + bitcoin-server: + container_name: bitcoin-server + hostname: bitcoin-server + image: libbitcoin/bitcoin-server:${VERSION_BITCOIN_SERVER} + # restart: unless-stopped + ports: + - 8333:8333/tcp + - 8333:8333/udp + - 9081:9081/tcp + - 9082:9082/tcp + - 9083:9083/tcp + - 9084:9084/tcp + - 9091:9091/tcp + - 9092:9092/tcp + - 9093:9093/tcp + - 9094:9094/tcp + volumes: + - ${STORAGE_BITCOIN_DATA}:/bitcoin/blockchain + - ${STORAGE_BITCOIN_CONF}:/bitcoin/conf + networks: + - net diff --git a/templates/gsl.developer_setup.sh b/templates/gsl.developer_setup.sh index 64781df..976195b 100644 --- a/templates/gsl.developer_setup.sh +++ b/templates/gsl.developer_setup.sh @@ -106,9 +106,8 @@ BUILD_TARGET="unknown" . heading2("Process script specific options.") handle_custom_options() { - if [[ ($BUILD_MODE != "sync") && ($BUILD_MODE != "configure") && ($BUILD_MODE != "build") ]]; then - display_error "Unsupported build-mode: $BUILD_MODE" - display_error "Supported values are: sync, configure, build" + if [[ ! ($BUILD_SRC_DIR) ]]; then + display_error "Missing build-src-dir required." display_error "" display_help exit 1 @@ -122,18 +121,21 @@ handle_custom_options() exit 1 fi - if [[ ! ($BUILD_SRC_DIR) ]]; then - display_error "Missing build-src-dir required." - display_error "" - display_help - exit 1 - fi + if [[ ! ($SYNC_ONLY) ]]; then + if [[ ($BUILD_MODE != "sync") && ($BUILD_MODE != "configure") && ($BUILD_MODE != "build") ]]; then + display_error "Unsupported build-mode: $BUILD_MODE" + display_error "Supported values are: sync, configure, build" + display_error "" + display_help + exit 1 + fi - if [[ ! ($BUILD_OBJ_DIR) ]]; then - display_error "Missing build-obj-dir required." - display_error "" - display_help - exit 1 + if [[ ! ($BUILD_OBJ_DIR) ]]; then + display_error "Missing build-obj-dir required." + display_error "" + display_help + exit 1 + fi fi if [[ $BUILD_TAG ]]; then @@ -498,7 +500,9 @@ else display_configuration push_directory "$BUILD_SRC_DIR" initialize_git - initialize_object_directory + if [[ ! ($SYNC_ONLY) ]]; then + initialize_object_directory + fi create_local_copies if [[ $SYNC_ONLY ]]; then display_message "Skipping build due to --sync-only."