Docker compose部署Golang服务

Docker Compose 部署

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

定义 Dockerfile

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

dockerfile 复制代码
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三个容器,配置以及说明如下:

yaml 复制代码
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 文件

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
构建启动
sh 复制代码
# 会根据 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进入,执行以下操作:

sh 复制代码
> use mysql;
> update user set host = '%' where user = 'root';
> FLUSH PRIVILEGES;

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

sh 复制代码
> use mysql;
> ALTER USER 'root'@'%' IDENTIFIED WITH mysql_native_password BY 'root123456';
> FLUSH PRIVILEGES;

文章推荐:

相关推荐
一水鉴天7 分钟前
整体设计 逻辑系统程序 之18 Source 容器(Docker)承载 C/P/D 三式的完整设计与双闭环验证 之2
docker·架构·认知科学·公共逻辑
飞快的蜗牛2 小时前
利用linux系统自带的cron 定时备份数据库,不需要写代码了
java·docker
火星MARK2 小时前
k8s面试题
容器·面试·kubernetes
香吧香3 小时前
Docker Registry 使用总结
docker
赵渝强老师3 小时前
【赵渝强老师】Docker容器的资源管理机制
linux·docker·容器·kubernetes
haicome5 小时前
deepseek部署
docker·ragflow·deepseek 部署
乄bluefox5 小时前
保姆级docker部署nacos集群
java·docker·容器
每天进步一点_JL5 小时前
Docker 是什么?
后端·docker·容器
一叶飘零_sweeeet6 小时前
从 0 到 1 掌控云原生部署:Java 项目的 Docker 容器化与 K8s 集群实战指南
docker·云原生·kubernetes·项目部署
森林猿6 小时前
docker-compose-kafka 4.1.0
docker·容器·kafka