前言

在服务都趋向于 Go 语言化之后,遇到的第一个问题就是如何部署 Go 项目的二进制?我们一路走来爬了很多坑,总结出来的部署方式如下:

  • 将二进制可执行文件直接复制到目标服务器,然后通过 Shell 脚本管理服务进程
  • 将二进制可执行文件直接复制到目标服务器,通过预定义的 Service 配置文件,让 Systemd 管理服务进程
  • 使用 Docker Image,在单台服务器上用 Docker CLI 管理服务进程
  • 使用 Docker Image,通过容器编排与调度平台实现灰度发布

前三种方式有点是对于基础设施没啥要求,但是缺点也很明显,很难做到用户无感知的代码发布,除非在项目代码中集成一些第三方的服务注册与发现的基础设施,如 Consul 或 ETCD 等!

为了能顺利过渡到容器编排与调度,我们首先要做的就是从传统的二进制分发方式转变为 Docker Image 分发的方式,那么这就需要用到 Docker Container Registry。

对于代码管理用的是 Self-Hosted Gitlab 的团队而言,Gitlab Container Registry,是一个比较不错的选择,Gitlab 已经内置,只需要通过配置开启即可!

配置

Gitlab 服务相关配置目录结构如下:

tree -a -I .git
.
├── .env
├── .gitignore
├── README.md
└── docker-compose.yml

1 directory, 5 files

.env 配置如下:

# Gitlab external access url
EXTERNAL_URL=https://gitlab.local

# Gitlab Pages external access url
PAGES_EXTERNAL_URL=https://pages.local

# Gitlab Container Registry external access url
REGISTRY_EXTERNAL_URL=https://registry.local

# The maximum size of the log before it is rolled. A positive integer plus a modifier representing the unit of measure (k, m, or g). Defaults to 20m.
GITLAB_CONTAINER_LOG_MAX_SIZE=1024m

# SSL Certificates
# SSL_CERTIFICATE=/etc/gitlab/ssl/fullchain.cer
# SSL_CERTIFICATE_KEY=/etc/gitlab/ssl/fullchain.key

# Email configuration
EMAIL_ENABLED=true
[email protected]
EMAIL_DISPLAY_NAME=Gitlab
[email protected]

# SMTP configuration. Related settings for other service providers please refer to https://docs.gitlab.com/omnibus/settings/smtp.html#example-configurations
SMTP_ENABLE=true
SMTP_ADDRESS=smtp.example.com
SMTP_PORT=465
[email protected]
[email protected]
SMTP_DOMAIN=smtp.example.com
SMTP_AUTHENTICATION=login
SMTP_ENABLE_STARTTLS_AUTO=false
SMTP_TLS=true

docker-compose.yaml 配置如下:

services:
  gitlab:
    image: 'gitlab/gitlab-ee:latest'
    restart: no
    logging:
      options:
        max-size: ${GITLAB_CONTAINER_LOG_MAX_SIZE}
    hostname: gitlab
    container_name: gitlab
    networks:
      - traefik
    labels:
      - traefik.enable=true

      - traefik.http.routers.pages.tls=true
      - traefik.http.routers.pages.rule=Host(`pages.local`)
      - traefik.http.routers.pages.service=pages
      - traefik.http.routers.pages.entrypoints=http,https

      - traefik.http.routers.gitlab.tls=true
      - traefik.http.routers.gitlab.rule=Host(`gitlab.local`)
      - traefik.http.routers.gitlab.service=gitlab
      - traefik.http.routers.gitlab.entrypoints=http,https

      - traefik.http.routers.registry.tls=true
      - traefik.http.routers.registry.rule=Host(`registry.local`)
      - traefik.http.routers.registry.service=registry
      - traefik.http.routers.registry.entrypoints=http,https

      - traefik.http.services.pages.loadbalancer.server.port=80
      - traefik.http.services.gitlab.loadbalancer.server.port=80
      - traefik.http.services.registry.loadbalancer.server.port=5100

    environment:
      TZ: Asia/Shanghai
      GITLAB_LOG_LEVEL: error
      GITLAB_OMNIBUS_CONFIG: |
        external_url '$EXTERNAL_URL';
        nginx['enable'] = true;
        nginx['listen_port'] = 80
        nginx['listen_https'] = false
        nginx['redirect_http_to_https'] = true
        nginx['client_max_body_size'] = '1024m';
        nginx['proxy_set_headers'] = {
          "Host" => "$$http_host",
          "Upgrade" => "$$http_upgrade",
          "X-Real-IP" => "$$remote_addr",
          "Connection" => "Upgrade",
          "X-Forwarded-For" => "$$proxy_add_x_forwarded_for",
          "X-Forwarded-Ssl" => "on",
          "X-Forwarded-Proto" => "https"
        };

        registry['enable'] = true
        registry_external_url '$REGISTRY_EXTERNAL_URL'
        registry_nginx['listen_port'] = 5100
        registry_nginx['listen_https'] = false
        registry_nginx['redirect_http_to_https'] = true
        registry_nginx['proxy_set_headers'] = {
          "Host" => "$$http_host",
          "X-Real-IP" => "$$remote_addr",
          "X-Forwarded-For" => "$$proxy_add_x_forwarded_for",
          "X-Forwarded-Ssl" => "on",
          "X-Forwarded-Proto" => "https"
        }

        pages_external_url '$PAGES_EXTERNAL_URL';
        gitlab_pages['enable'] = true;
        gitlab_pages['listen_https'] = false;
        gitlab_pages['inplace_chroot'] = true;
        gitlab_pages['access_control'] = true;
        pages_nginx['enable'] = true;
        pages_nginx['listen_port'] = 80
        pages_nginx['listen_https'] = false
        pages_nginx['redirect_http_to_https'] = true

        gitlab_rails['time_zone'] = 'Asia/Shanghai';
        gitlab_rails['lfs_enabled'] = true;
        gitlab_rails['gitlab_email_enabled'] = true;
        gitlab_rails['gitlab_email_from'] = '$EMAIL_FROM';
        gitlab_rails['gitlab_email_display_name'] = '$EMAIL_DISPLAY_NAME';
        gitlab_rails['gitlab_email_reply_to'] = '$EMAIL_REPLY_TO';
        gitlab_rails['smtp_enable'] = $SMTP_ENABLE;
        gitlab_rails['smtp_address'] = '$SMTP_ADDRESS';
        gitlab_rails['smtp_port'] = $SMTP_PORT;
        gitlab_rails['smtp_user_name'] = '$SMTP_USER_NAME';
        gitlab_rails['smtp_password'] = '$SMTP_PASSWORD';
        gitlab_rails['smtp_domain'] = '$SMTP_DOMAIN';
        gitlab_rails['smtp_authentication'] = '$SMTP_AUTHENTICATION';
        gitlab_rails['smtp_enable_starttls_auto'] = $SMTP_ENABLE_STARTTLS_AUTO;
        gitlab_rails['smtp_tls'] = $SMTP_TLS;
    ports:
      - "0.0.0.0:22:22"
    volumes:
      - gitlab-conf:/etc/gitlab
      - gitlab-logs:/var/log/gitlab
      - gitlab-data:/var/opt/gitlab

volumes:
  gitlab-conf:
    external: true
  gitlab-logs:
    external: true
  gitlab-data:
    external: true

networks:
  traefik:
    external: true
  • 第 12~32 行 labels 用于向 Traefik 注册的反向代理。
  • 第 54~64 行中的配置是用于开启 Gitlab Container Registry,以及配置 Nginx 反向代理。

我在本地搭建的所有的域名都用的 .local 这个 TLD。

启动服务

docker compose up -d

测试

访问 Gitlab 中项目的 Container Registry 页面:

OrbStack Config

登录

docker login registry.local
Username: [email protected]
Password:
Login Succeeded

注意:这里如果域名不是公网可解析的域名,需要自己设置 DNS 或者设置 Docker Daemon 的 Proxy ,才能正常登录!将 registry.local 替换为自己的域名。

OrbStack Config

Push Image

我这里直接将 Traefik 官方的镜像重新打个 Tag 然后推送到 Gitlab Container Registry 中:

docker tag traefik:latest registry.local/devops/traefik:latest
docker push registry.local/devops/traefik:latest
The push refers to repository [registry.local/devops/traefik]
ef79cc9bf4db: Pushed
684de1f4a5df: Pushed
7f5495899e4d: Pushed
5f4d9fc4d98d: Pushed
latest: digest: sha256:4d7c60c1d882dafc8f0148231d836f7d1e2fef0d732daf1cd16f37d711afe689 size: 1157

然后再来看看项目中的 Container Registry 就有了刚才 Push 的 Image:

OrbStack Config

总结

这只是服务容器化迈出的第一步,要想服务能够更好的发布,还需要更强大的编排和调度系统!

I hope this is helpful, Happy hacking…