利用docker部署一个简单的go服务

本文最后更新于:2023年4月17日 晚上

一直在用很多docker的image, 但是没有自己实际构建过一个镜像, 今天打算写一个小demo试试手, 并总结一下遇到的一些问题

项目GITHUB地址

build & push

Dockerfile: 两种构建方式

docker通过自己写一个叫做Dockerfile的东西进行镜像的构建

对于一些语言, 可以提前交叉编译好对应的可执行文件, 就避免了在docker中pull一些比较大的image

比如对于一个go项目, 我们可以通过

1
2
3
4
5
6
7
8
9
10
11
12
13
FROM golang:1.17 as mod
ENV GOPROXY https://goproxy.cn
WORKDIR /root/

COPY ./ ./

COPY go.mod ./
RUN go mod download

RUN CGO_ENABLED=0 GOOS=linux GOARCH=amd64 go build -o main main.go

EXPOSE 8888
ENTRYPOINT ["./main"]

这种方式构建一个image, 也就是先下一个golang的image然后在其中构建出可执行文件

然而我们上dockerhub可以看到这个image大小为几百兆到几个G

而我们可以选择本地交叉编译出linux的可执行文件, 然后直接拷贝到image中, 也就是写这样一个dockerfile:

1
2
3
4
FROM alpine:latest
COPY ./main /main
EXPOSE 8888
CMD ["./main"]

而main的交叉编译步骤如下:

1
2
3
4
set CGO_ENABLED=0
set GOARCH=amd64
set GOOS=linux
go build -o main main.go

这里注意一下有个坑卡了我好久, 就是在windows下, 如果你用的是cmd, 则使用set XX=yy来设置临时环境变量

如果你使用的是powershell, 则应该用$env:XX=yy来设置临时环境变量

我当时powershell下set了半天发现build出的东西死活没法跑在docker里, 也没有任何报错…

这样依赖的image叫alpine, 是一个微型的linux镜像, 仅仅只有5MB的大小

docker build / push

在写好Dockerfile后, 我们可以通过

docker build -t image_name .

其中-t表示tag的意思, 即给构建出的镜像取一个名字

之后便可以登录到dockerhub, 然后发布自己的镜像:

1
2
3
docker login -u {{user_name}} -p {{password}}
docker tag {{image_name}} {{user_name/image_name}}
docker push {{user_name/image_name}}

如:

1
2
3
docker login -u {{user_name}} -p {{password}}
docker tag server moreality/pastebin-server
docker push moreality/pastebin-server

之后便可以到dockerhub上查看自己发布的镜像了

docker-compose

Compose 是用于定义和运行多容器 Docker 应用程序的工具。通过 Compose,您可以使用 YML 文件来配置应用程序需要的所有服务。然后,使用一个命令,就可以从 YML 文件配置中创建并启动所有服务。

比如我写的pastebin-server这个demo不仅有go的web部分, 还依赖了redis

这时我就可以通过docker-compose将我的pastebin-serverredis整合在一起运行

具体例子如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
version: '3'
services:
redis:
image: redis:7.0.4
restart: "no"
container_name: redis
ports:
- "6379:6379"
volumes:
- ./redis.conf:/etc/redis/redis.conf
- ./data:/data
command: redis-server /etc/redis/redis.conf --appendonly yes
privileged: true
networks:
- server
pastebin-server:
image: moreality/pastebin-server:latest
container_name: pastebin
depends_on:
- "redis"
ports:
- "9989:9989"
networks:
- server
volumes:
- ./config.yml:/config.yml

networks:
server:
driver:
bridge

进入容器

可以使用docker exec -it <container-id/container-name> bash进入容器bash交互

实现容器启动先后关系

可以通过depends_on表明本容器依赖于哪个容器的先启动, 如我的pastebin-server需要先启动redis

实现容器的互通

注意docker的多个容器如果不进行配置他们的网络环境是互相隔离的, 也就是说pastebin-server并不能通过localhost:6379就直接访问到redis, 我们可以通过networks设置将多个容器互联起来, 上面的配置文件展示了如何互联

在进行上面的配置后, docker会自动为主机创建别名, 如在pastebin-serverping redis是可以直接ping通的

也即通过redis:6379就可以连接到redis服务

服务器部署

在了解了构建和docker-compose后, 服务器上的操作就变得简单了起来, 不需要手动上传什么文件了

直接将docker-compose的内容复制到服务器上, 然后安装docker和docker-compose

首先启动docker服务, 然后docker-compose up

就大功告成了

(附) nginx配置https

其实上面已经可以使用常规的ip + port方式访问服务了, 这里记录一下简单的nginx的https配置 (ubuntu为例)

首先你需要:

  • 一个域名
  • 一个SSL证书 (一般是pem + key)
  • sudo apt-install nginx

然后vim /etc/nginx/nginx.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
events {
worker_connections 1024; # 并发数
}
http {
server {
listen 443 ssl; #监听端口
server_name your.servername.com; #请求域名
ssl on; #开启ssl
ssl_certificate /path/to/your/cert.pem; #证书路径
ssl_certificate_key /path/to/your/cert.key; #证书key路径
ssl_session_timeout 5m; #会话超时时间
ssl_ciphers ECDHE-RSA-AES128-GCM-SHA256:ECDHE:ECDH:AES:HIGH:!NULL:!aNULL:!MD5:!ADH:!RC4; #加密算法
ssl_protocols TLSv1 TLSv1.1 TLSv1.2; #SSL协议

# 拦截所有请求, 转发到服务地址
location / {
proxy_pass http://ip:port;
}
}
}

然后可以nginx -t验证一下配置文件的语法是否合法

1
2
root@moreality:/home/cert# nginx -t
nginx: the configuration file /etc/nginx/nginx.conf syntax is ok

最后systemctl restart nginx即可

小插曲

部署到服务器后我尝试插入一句话, 然后redis就炸了, 原因是OOM

error: OOM command not allowed when used memory > 'maxmemory'.

结果我进入容器查看一下maxmemory还没发现问题:

1
2
3
4
5
6
docker exec -it redis bash
redis-cli
auto <password>
config get maxmemory
1) "maxmemory"
2) "512"

原因是我maxmemory设置的值是512

我不知道从哪个邪乎博客(不看官方文档和stackoverflow的下场)看到单位是MB, 然后我就以为是512MB

结果存的是512B…

改成maxmemory 512MB, 再次config get maxmemory就显示了

1
2
3
> config get maxmemory
1) "maxmemory"
2) "536870912"

没什么问题了


利用docker部署一个简单的go服务
https://blog.roccoshi.top/posts/40643/
作者
RoccoShi
发布于
2022年8月15日
许可协议