在docker部署时,除了使用--link的方式来关联容器之外,还可以使用 docker compose 运行多个容器。

本文以项目:https://github.com/johncxf/go-api 为例。

定义 Dockerfile

我这里用于区分默认 Dockerfile 文件,在项目根目录下新建一个 Dockerfile-compose 文件:

FROM golang:alpine AS builder

# 在容器内部设置环境变量
ENV GO111MODULE=on \
    GOPROXY=https://goproxy.cn,direct \
    CGO_ENABLED=0 \
    GOOS=linux \
    GOARCH=amd64

# 设置后续指令的工作目录
WORKDIR /build

# 复制项目中的 go.mod 和 go.sum文件并下载依赖信息
COPY go.mod .
COPY go.sum .
RUN go mod download

# 将代码复制到容器中
COPY . .

# 将代码编译成二进制可执行文件
RUN go build -o go-api .


# 创建一个小镜像
#FROM scratch
FROM debian:stretch-slim

COPY ./config /config

# 从builder镜像中把 /build/go-api 拷贝到当前目录
COPY --from=builder /build/go-api /

# 需要运行的命令(docker compose 运行不需要执行这一行)
#ENTRYPOINT ["/go-api", "config/env.yml"]

docker-compose.yml

新建 docker-compose.yml配置文件与项目根目录下。

我这里配置了mysql、redis、go-api三个容器,配置以及说明如下:

version: "3.7"
services:
  mysql:
    # 镜像版本号
    image: mysql:8.0.33
    # 容器名
    container_name: go-web-mysql
    # 端口号映射
    ports:
      - "8306:3306"
    # 失败后总是重启
    restart: "always"
    command: "--default-authentication-plugin=mysql_native_password --init-file /data/application/init.sql"
    environment:
      MYSQL_ROOT_PASSWORD: "root123456" # root 账号密码
      MYSQL_DATABASE: "test"            # 数据库
    # 将mysql相关数据挂载到本机目录
    volumes:
      - ~/docker-data/go-api/mysql/init.sql:/data/application/init.sql
      - ~/docker-data/go-api/mysql/data:/var/lib/mysql           #数据文件挂载
      - ~/docker-data/go-api/mysql/conf.d:/etc/mysql/conf.d      #配置文件挂载
      - ~/docker-data/go-api/mysql/log:/var/log/mysql            #日志文件挂载
  redis:
    # 镜像版本号
    image: redis:7.2.4
    # 容器名
    container_name: go-web-redis
    # 端口号
    ports:
      - "6379:6379"
    # 失败后总是重启
    restart: "always"
    # 以配置文件的方式启动 redis.conf
    command: "redis-server /etc/redis/redis.conf --appendonly yes --requirepass root123456"
    # 文件夹以及文件映射
    volumes:
      - ~/docker-data/go-api/redis:/data
      - ~/docker-data/go-api/redis/redis.conf:/etc/redis/redis.conf
  go-api:
    # 容器名
    container_name: go-web-api
    build:
      context: .
      dockerfile: Dockerfile-compose  # 默认为 Dockerfile,这里重新定义为 Dockerfile-compose 文件
    # 失败后总是重启
    restart: "always"
    #    command: sh -c "./wait-for-it.sh mysql:3306 -- ./go-api ./config/env.yml"
    command: [ "/wait-for-it.sh", "mysql:3306", "--", "/go-api", "config/env.yml" ]
    # 依赖启动项
    depends_on:
      - mysql
      - redis
    # 端口映射
    ports:
      - "8888:8088"

Mysql 状态检测

docker-compose.yml 配置文件中 depends_on字段仅能保证web服务启动时,mysql服务处于Running状态而不是Ready状态,因为go-api需要等待mysql启动后再启动,因此需要添加一个wait-for-it.sh脚本文件,检测mysql服务是否处于Ready状态。

在项目根目录下新建 wait-for-it.sh 文件

#!/usr/bin/env bash
# Use this script to test if a given TCP host/port are available

WAITFORIT_cmdname=${0##*/}

echoerr() { if [[ $WAITFORIT_QUIET -ne 1 ]]; then echo "$@" 1>&2; fi }

usage()
{
    cat << USAGE >&2
Usage:
    $WAITFORIT_cmdname host:port [-s] [-t timeout] [-- command args]
    -h HOST | --host=HOST       Host or IP under test
    -p PORT | --port=PORT       TCP port under test
                                Alternatively, you specify the host and port as host:port
    -s | --strict               Only execute subcommand if the test succeeds
    -q | --quiet                Don't output any status messages
    -t TIMEOUT | --timeout=TIMEOUT
                                Timeout in seconds, zero for no timeout
    -- COMMAND ARGS             Execute command with args after the test finishes
USAGE
    exit 1
}

wait_for()
{
    if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
        echoerr "$WAITFORIT_cmdname: waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
    else
        echoerr "$WAITFORIT_cmdname: waiting for $WAITFORIT_HOST:$WAITFORIT_PORT without a timeout"
    fi
    WAITFORIT_start_ts=$(date +%s)
    while :
    do
        if [[ $WAITFORIT_ISBUSY -eq 1 ]]; then
            nc -z $WAITFORIT_HOST $WAITFORIT_PORT
            WAITFORIT_result=$?
        else
            (echo -n > /dev/tcp/$WAITFORIT_HOST/$WAITFORIT_PORT) >/dev/null 2>&1
            WAITFORIT_result=$?
        fi
        if [[ $WAITFORIT_result -eq 0 ]]; then
            WAITFORIT_end_ts=$(date +%s)
            echoerr "$WAITFORIT_cmdname: $WAITFORIT_HOST:$WAITFORIT_PORT is available after $((WAITFORIT_end_ts - WAITFORIT_start_ts)) seconds"
            break
        fi
        sleep 1
    done
    return $WAITFORIT_result
}

wait_for_wrapper()
{
    # In order to support SIGINT during timeout: http://unix.stackexchange.com/a/57692
    if [[ $WAITFORIT_QUIET -eq 1 ]]; then
        timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --quiet --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
    else
        timeout $WAITFORIT_BUSYTIMEFLAG $WAITFORIT_TIMEOUT $0 --child --host=$WAITFORIT_HOST --port=$WAITFORIT_PORT --timeout=$WAITFORIT_TIMEOUT &
    fi
    WAITFORIT_PID=$!
    trap "kill -INT -$WAITFORIT_PID" INT
    wait $WAITFORIT_PID
    WAITFORIT_RESULT=$?
    if [[ $WAITFORIT_RESULT -ne 0 ]]; then
        echoerr "$WAITFORIT_cmdname: timeout occurred after waiting $WAITFORIT_TIMEOUT seconds for $WAITFORIT_HOST:$WAITFORIT_PORT"
    fi
    return $WAITFORIT_RESULT
}

# process arguments
while [[ $# -gt 0 ]]
do
    case "$1" in
        *:* )
        WAITFORIT_hostport=(${1//:/ })
        WAITFORIT_HOST=${WAITFORIT_hostport[0]}
        WAITFORIT_PORT=${WAITFORIT_hostport[1]}
        shift 1
        ;;
        --child)
        WAITFORIT_CHILD=1
        shift 1
        ;;
        -q | --quiet)
        WAITFORIT_QUIET=1
        shift 1
        ;;
        -s | --strict)
        WAITFORIT_STRICT=1
        shift 1
        ;;
        -h)
        WAITFORIT_HOST="$2"
        if [[ $WAITFORIT_HOST == "" ]]; then break; fi
        shift 2
        ;;
        --host=*)
        WAITFORIT_HOST="${1#*=}"
        shift 1
        ;;
        -p)
        WAITFORIT_PORT="$2"
        if [[ $WAITFORIT_PORT == "" ]]; then break; fi
        shift 2
        ;;
        --port=*)
        WAITFORIT_PORT="${1#*=}"
        shift 1
        ;;
        -t)
        WAITFORIT_TIMEOUT="$2"
        if [[ $WAITFORIT_TIMEOUT == "" ]]; then break; fi
        shift 2
        ;;
        --timeout=*)
        WAITFORIT_TIMEOUT="${1#*=}"
        shift 1
        ;;
        --)
        shift
        WAITFORIT_CLI=("$@")
        break
        ;;
        --help)
        usage
        ;;
        *)
        echoerr "Unknown argument: $1"
        usage
        ;;
    esac
done

if [[ "$WAITFORIT_HOST" == "" || "$WAITFORIT_PORT" == "" ]]; then
    echoerr "Error: you need to provide a host and port to test."
    usage
fi

WAITFORIT_TIMEOUT=${WAITFORIT_TIMEOUT:-15}
WAITFORIT_STRICT=${WAITFORIT_STRICT:-0}
WAITFORIT_CHILD=${WAITFORIT_CHILD:-0}
WAITFORIT_QUIET=${WAITFORIT_QUIET:-0}

# Check to see if timeout is from busybox?
WAITFORIT_TIMEOUT_PATH=$(type -p timeout)
WAITFORIT_TIMEOUT_PATH=$(realpath $WAITFORIT_TIMEOUT_PATH 2>/dev/null || readlink -f $WAITFORIT_TIMEOUT_PATH)

WAITFORIT_BUSYTIMEFLAG=""
if [[ $WAITFORIT_TIMEOUT_PATH =~ "busybox" ]]; then
    WAITFORIT_ISBUSY=1
    # Check if busybox timeout uses -t flag
    # (recent Alpine versions don't support -t anymore)
    if timeout &>/dev/stdout | grep -q -e '-t '; then
        WAITFORIT_BUSYTIMEFLAG="-t"
    fi
else
    WAITFORIT_ISBUSY=0
fi

if [[ $WAITFORIT_CHILD -gt 0 ]]; then
    wait_for
    WAITFORIT_RESULT=$?
    exit $WAITFORIT_RESULT
else
    if [[ $WAITFORIT_TIMEOUT -gt 0 ]]; then
        wait_for_wrapper
        WAITFORIT_RESULT=$?
    else
        wait_for
        WAITFORIT_RESULT=$?
    fi
fi

if [[ $WAITFORIT_CLI != "" ]]; then
    if [[ $WAITFORIT_RESULT -ne 0 && $WAITFORIT_STRICT -eq 1 ]]; then
        echoerr "$WAITFORIT_cmdname: strict mode, refusing to execute subprocess"
        exit $WAITFORIT_RESULT
    fi
    exec "${WAITFORIT_CLI[@]}"
else
    exit $WAITFORIT_RESULT
fi

构建启动

# 会根据 docker-compose 文件,构建镜像,并启动所有容器
$ docker-compose up -d

# 查看容器
$ docker-compose ps -a

# 停止所有容器
$ docker-compose down

启动成功后,接下来就可以通过http://127.0.0.1:8888进行访问了。

如果连接 Mysql 出现报错,可以参考:

Msql 进入容器使用 127.0.0.1连接mysql报错:ERROR 1130 (HY000): Host '127.0.0.1' is not allowed to connect to this MySQL server,可以直接输入 mysql进入,执行以下操作:

> use mysql;
> update user set host = '%' where user = 'root';
> FLUSH PRIVILEGES;

Mysql root 密码设置不生效解决:

> use mysql;
> ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root123456';
> FLUSH PRIVILEGES;

推荐文章:Go服务部署-Docker部署


本文由 一切随风 创作,可自由转载、引用,但需署名作者且注明文章出处。

15 条评论

  1. foqlnowqsh
    foqlnowqsh

    白发魔女传

  2. omdenhiuop
    omdenhiuop

    暗影之地

  3. ndwubzkrlj
    ndwubzkrlj

    海滩救护队

  4. huuquitrmk
    huuquitrmk

    新警察故事

  5. zxavdfqozr
    zxavdfqozr

    css教程

  6. pxnehimacg
    pxnehimacg

    鬼眼日记

  7. ugvthausmq
    ugvthausmq

    911世界停滞之日

  8. qnvaovbccq
    qnvaovbccq

    因果报应

  9. ythgulkwwz
    ythgulkwwz

    你的才华让人瞩目,期待你的更多文章。 http://www.55baobei.com/hD9XdToPIf.html

  10. fsrdvgvmzw
    fsrdvgvmzw

    《比尔伯尔:纸老虎》欧美综艺高清在线免费观看:https://www.jgz518.com/xingkong/121683.html

  11. upqpgsrdbi
    upqpgsrdbi

    你的才华横溢,让人敬佩。 https://www.yonboz.com/video/56230.html

  12. bkqxppgmkc
    bkqxppgmkc

    《迷离无迹》恐怖片高清在线免费观看:https://www.jgz518.com/xingkong/16049.html

  13. cbpbazxjji
    cbpbazxjji

    你的文章让我感受到了正能量,非常棒! https://www.yonboz.com/video/74479.html

  14. kztzexvuuc
    kztzexvuuc

    《大汉天子2:汉武雄风》国产剧高清在线免费观看:https://www.jgz518.com/xingkong/37626.html

  15. fxohdefnui
    fxohdefnui

    想想你的文章写的特别好https://www.ea55.com/

添加新评论