Write the Code. Change the World.

分类目录
1月 10

基础镜像构建好了之后,就可以使用镜像来构建容器,从而来进行服务。这里想创建一个 websocket 服务的项目。

这里可以有几个文章可以参考:

https://blog.csdn.net/linxinfa/article/details/120573016

https://github.com/Skycrab/skynet_websocket

[https://blog.csdn.net/u013617851/article/details/86712243][https://blog.csdn.net/u013617851/article/details/86712243]

创建基础文件夹和文件

按照 https://blog.csdn.net/linxinfa/article/details/120573016 的博客,对文件进行分类安排,创建基础文件夹和文件。如下所示:

│  .dockerignore
│  docker-compose.yaml
│
├─service
│  └─skynet
│          Dockerfile
│
└─www
    └─websocket
        │  main.lua
        │
        ├─etc
        │      cluster.lua
        │      config
        │
        ├─luaclib
        ├─lualib
        └─service

对于 skynet, config 文件会作为入口文件。而这个 config 文件可以是项目中任何位置,因为是由 skynet config 来明确指定的。不过,依照前人的约定习惯,把 config 文件放在 ./websocket/etc/ 下。

再来全局来看项目结构。最外层是 docker 相关的配置。 www 目录是项目的总和。这样是方便存放项目。今天存放一个 websocket 的项目,明天可能就会存放 socket.io 的项目。

在 websocket 项目中结果,还是遵循前人的约定习惯。

到此,仅可以看到创建的一些文件夹和两个文件:config 文件和 main.lua 文件。cluster.lua 暂时是空的。

config 文件你也可以命名为 config.lua 文件。

config

config.lua 作为入口文件,其地位当然是举足轻重的。来看看里边的内容。

-- 根目录
skynet_path = '/skynet'
service_path = '/www/websocket'

-- 节点名称
node = 'node_1'

-- 启用工作线程数 | c 编写的服务模块的位置 | 启动第一个服务
thread = 8
cpath = skynet_path .. '/cservice/?.so' 
bootstrap = 'snlua bootstrap'

-- 主程序入库 | 适用 cluster 的集群模式
start = 'main'
harbor = 0

-- lua 配置项
lualoader = skynet_path .. '/lualib/loader.lua'
luaservice = skynet_path .. "/service/?.lua;" .. service_path .. "/?.lua"
lua_path = skynet_path .. "/lualib/?.lua;" .. skynet_path .. "/lualib/?/init.lua;" .. service_path .. "/?.lua"
lua_cpath = skynet_path .. "/luaclib/?.so;" .. service_path .. "/luaclib/?.so"

skynet_path 配置的是 skynet 的安装的位置。

service_path 配置的是项目的位置。

这两个位置下边的配置会用到。

代码中,也加了一些注释说明。下边用表格的方式,再说明一次。

参数 描述
thread 启用的工作线程数量,一般配置为CPU核心数
harbor 主从节点模式。skynet 初期提供了 master/slave 集群模式,后来提供了更适用的 cluster 集群模式,建议使用 cluster 模式,配 0
cpath C 语言编写的服务模块的路径。cpath 配置项通常用于指定 C 模块的搜索路径。它不是必须的,但是如果你需要在你的服务中使用 C 编写的模块,那么你需要配置 cpath 来指定这些模块的位置。cpath 应该放在 bootstrap 配置之前,因为 bootstrap 会使用 cpath 指定的路径来加载 C 模块。如果你没有配置 cpath,那么你的服务将无法找到和加载C模块。因此,根据你的需求,你可能需要根据你的具体路径来配置 cpath
bootstrap 指定 skynet 启动时的第一个服务以及其启动参数。默认配置为 snlua bootstrap,即启动一个名为 bootstrap 的lua服务,通常指的是 service/bootstrap.lua 这段代码。这个服务是启动其他服务的入口点,并且会根据配置文件中的其他参数来加载和启动其他服务。因此,bootstrap 是用于引导 skynet 系统启动的重要配置选项。
start 主服务入口
luaservice 用于指定 Lua 服务的路径。它通常是一个字符串,包含了服务的名称或路径。在配置文件中,你可以指定多个服务,用分号(;)分隔。(包括 skynet 框架自带的一些服务和自己写的服务。)
lualoader lua 脚本加载器。用于配置调用哪一段Lua代码加载Lua服务。通常配置为lualib/loader.lua,由这段代码解析服务名称,进一步加载Lua代码。snlua会将核心配置项取出,放置在初始化好的Lua虚拟机的全局变量中。
lua_path lua_path用于指定Lua模块的搜索路径。它通常是一个字符串,包含了多个路径,用分号(;)分隔。
lua_cpath 用于指定C模块的搜索路径。它通常是一个字符串,包含了多个路径,用分号(;)分隔。

最后啰嗦一下。在 config 中配置的信息,在后续的逻辑中都可以通过 skynet.getenv 方法获取到。在这里,不需要使用 skynet.setenv 方法来明确设置。

skynet.setenv 有一个严格的问题,就是不能对已存在的变量进行设置。相对于一个 const 的行为。

虽然吧,在其他地方通过 skynet.getenv 方法获取到。但是也能通过 skynet.setenv 进行赋值一次。再第二次赋值就会报错。

从显现来看,虽然可以通过 skynet.getenv 方法获取到 config 中的参数,那似乎不是通过 skynet.setenv 的方式来传递的。

再来看 main.lua 中的代码。

local skynet = require 'skynet'

skynet.start(function()
    skynet.error('hello world')
    skynet.error(skynet.exit())
end)

就这么多。

代码都准备好了,最后在 docker-compose.yaml 文件中添加使用镜像,构建容器,启动服务的配置。

  …
  websocket:
    image: skynet:1.7
    container_name: websocket
    volumes:
      - ./www:/www
    command: /skynet/skynet /www/websocket/etc/config
    depends_on:
      - skynet

运行 docker-compose up 看看 。

(base) PS D:\Study\skynet\websocket> docker-compose up
[+] Building 0.0s (0/0)                                                                                                                                                                 docker:default 
[+] Running 2/0
 ✔ Container skynet1.7  Created                                                                                                                                                                   0.0s 
 ✔ Container websocket  Created                                                                                                                                                                   0.0s 
Attaching to skynet1.7, websocket
skynet1.7 exited with code 0
websocket  | [:00000002] LAUNCH snlua bootstrap
websocket  | [:00000003] LAUNCH snlua launcher   
websocket  | [:00000004] LAUNCH snlua cdummy     
websocket  | [:00000005] LAUNCH harbor 0 4       
websocket  | [:00000006] LAUNCH snlua datacenterd
websocket  | [:00000007] LAUNCH snlua service_mgr
websocket  | [:00000008] LAUNCH snlua main       
websocket  | [:00000008] hello world
websocket  | [:00000008] KILL self
websocket  | [:00000002] KILL self

是不是已经运行起来了,也达到了预期的效果了。

提交版本

git init -b main
git add .
git commit -m 'init skynet'
1月 10

用 docker 来部署和使用 skynet 其实也不错。

构建基础镜像

使用 docker init 命令创建基础的文件。将 Dockerfile 移动到 ./service/skynet/Dockerfile 这里。将 compose.yaml 文件重命名为 docker-compose.yaml

Dockerfile 文件中,做这些事情:使用一个基础镜像,安装 git,然后将 skynet 代码下下来,对代码进行编译。

Dockerfile

# syntax=docker/dockerfile:1

FROM alpine:latest

# 更新和安装 alpine 工具
RUN apk add --update alpine-sdk

RUN apk add --no-cache bash

RUN apk add readline-dev readline autoconf libgcc

# 安装 git
RUN apk add git

RUN git clone https://github.com/cloudwu/skynet.git /skynet

WORKDIR /skynet

RUN make linux

修改 compose.yaml 文件。

version: '3.9'
services:
  skynet: 
    build:
      context: ./service/skynet
    image: skynet:1.7
    container_name: skynet1.7

然后运行 docker-compose build 构建镜像。

为什么镜像的 tag 的版本号是 1.7 呢。因为 skynet 在 github 上最后的版本就是 1.7。

1月 09

https://github.com/coturn/coturn

这里使用 docker 来构建。

先准备一份 docker-compose.yaml 文件。

version: '3.9'
  services:
    coturn:
      image: coturn/coturn
      ports:
        - 3478:3478
        - 5349:5349
      volumes:
        - ./conf/coturn:/etc/coturn
      networks: webrtcnet
networks:
  webrtcnet:
    name: 'webrtcnet'
    driver: bridge

这里配置了端口映射和配置文件。当然配置文件可以不要,这样就空账号密码。ip 随主机 ip。这里加了 volumes, 可以有,但不一定要用。

./conf/coturn/turnserver.conf 配置文件。这里我全注释掉了。

# listening-port=3478
# tls-listening-port=5349       
# external-ip=47.**.**.81 
# user=17vision:17vision2024
# realm=17vision.com

然后执行 docker-compose up -d 运行起来。

最后,可以通过 https://webrtc.github.io/samples/src/content/peerconnection/trickle-ice/ 这个站点进行测试。

操作如下图所示:

相关文章

https://blog.csdn.net/codebooks/article/details/115757643

https://zhuanlan.zhihu.com/p/622818243

1月 06

有的时候只有一个域名。想让其他的服务也用这个域名和 80 端口。用 nginx 做代理就是一种方式。websocket 通常也会用到。

业务场景

在这里,有一个域名 xxx.com,通常使用 https 来访问,比如 https://xxx.com。加入 https://xxx.com, 已经有正在使用的业务。下边的场景都不能去干涉已有的业务场景。

  • 我有一个 node.js 的服务,其端口后是 3000,那么就可以通过代理来实现域名的访问。只是,这里要在 url 上做文章。 比如我请求 https://xxx.com/blog/xxx 这个服务,就让其转发到 node.js 的服务的 /xxx 上。那么,nginx 可以这样配置。
# Upgrade Connection websockets 和 升级的 http 协议才会用到
location ^~/blog/ {
    proxy_pass http://blog:3000/;
    proxy_http_version 1.1;
    proxy_set_header X-Real-IP $remote_addr;  
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  
    proxy_set_header Host $host;
    # proxy_set_header Upgrade $http_upgrade; 
    # proxy_set_header Connection 'upgrade';
    # proxy_cache_bypass $http_upgrade;
}

这里的代理 http://blog:3000/ 是因为 docker 构建的同一个网络中的服务叫做 blog,所以可以这么写。如果仅仅是宿主机,可以通过具体的 ip 来弄。

其实,之前我是想通过 rewrite 来实现。代码就是下边这样。只是这样虽然 url 修改了,但是似乎又重新发起请求了,并不是直接到代理那边。以至于添加的 location 无效。具体是什么情况,也不是很了解。毕竟 nginx 不精通,遇到具体的需要就去查询。所以想实现修改 url 又能实现代理,可以用上边的 location 语法。

# Upgrade Connection websockets 和 升级的 http 协议才会用到
location /blog {
    rewrite ^/dss(.*)$ $1 last;  
    proxy_pass http://blog:3000;
    proxy_http_version 1.1;
    proxy_set_header X-Real-IP $remote_addr;  
    proxy_set_header X-Forwarded-For $proxy_add_x_forwarded_for;  
    # proxy_set_header Upgrade $http_upgrade; 
    # proxy_set_header Host $host;
    # proxy_set_header Connection 'upgrade';
    # proxy_cache_bypass $http_upgrade;
}

主要是因为 node 那边定义的路由就是 /xxx/xxx, 没有 blog 这一节。

1月 03

使用 docker 是很方便,有的时候掉进去,却找不到原因。使用 docker-compose 对服务进行编排是一个很常见的场景。当执行 docker-compose up -d 的时候,无论是服务端代码,还是配置代码,都是正确的,日志也是正确的。可是容器总是会 Exited 状态。这个时候就很着急。其实,容器退出的原因一般是因为错误和不在前台运行导致的。既然没有错误,那估计就是容器没在前台运行。

今天使用 alpine 为基础镜像,安装一个 supervisord 作为守护进程,然后就是自己的业务环境 skynet 和 skynet 的业务代码。当以前都配置的很好,编写的很好,状态也很好的时候。容器就是退出。这个是一个很痛苦的事情。其实,还是对容器退出没理解透彻。

下边是启动时候,打印的一些日志。

nanjing_dev  | NanjingDevServer                 STARTING
nanjing_dev  | NanjingDevServer                 RUNNING   pid 14, uptime 0:00:50
nanjing_dev  | Proto Recv-Q Send-Q Local Address           Foreign Address         State
nanjing_dev  | tcp        0      0 127.0.0.11:33351        0.0.0.0:*               LISTEN
nanjing_dev  | tcp        0      0 0.0.0.0:8105            0.0.0.0:*               LISTEN
nanjing_dev  | udp        0      0 127.0.0.11:36173        0.0.0.0:*

没有任何错误。错误就在容器没有在前台运行。因为服务是用 supervisord 来启动的。让 supervisord 在前台运行不就好了。这个时候只需要修改 supervisor_xxx.ini 的配置文件,将启动方式改为前台即可。

[supervisord]
nodaemon=true

就是 nodaemon 设置为 true。 默认是 false 哈。

12月 25

在 Docker Compose 中,command字段定义的命令是在容器启动时执行的默认命令。这意味着每次启动或重启容器时,都会执行command中定义的命令。

如果你希望在容器创建时执行一次特定的命令,而不是每次启动都执行,可以使用以下方法:

使用 entrypoint 脚本:

在 Docker Compose 中,你可以通过创建一个自定义的 entrypoint 脚本来实现在容器创建时执行一次的操作。entrypoint 脚本是在容器启动时执行的脚本,通常被用于初始化工作。

例如,你可以创建一个名为 entrypoint.sh 的脚本文件,然后在 docker-compose.yml 文件中引用它:

version: '3'

services:
  my-service:
    build:
      context: .
    entrypoint: /app/entrypoint.sh
    # 其他服务配置...

在 entrypoint.sh 中,你可以编写需要在容器创建时执行的命令,并在最后调用 exec "$@" 以保持容器正常运行:

#!/bin/bash

# 在容器创建时执行的命令
echo "Initialization command"

# 执行 CMD 中定义的命令
exec "$@"

这样,entrypoint.sh 中的命令将只在容器创建时执行一次。

将 command 配置在 Dockerfile 的 cmd 中

如果你只希望在容器创建时执行一次特定的命令,而不是每次启动都执行,可以将该命令直接包含在 Dockerfile 中。在 Dockerfile 的 CMD 指令中设置你希望执行的命令,而不在 docker-compose.yml 中的 command 中设置。

Dockerfile 示例:

FROM your-base-image

# 在容器创建时执行的命令
CMD ["your-initialization-command"]

# 其他 Dockerfile 配置...

在这种情况下,CMD 中定义的命令将只在容器创建时执行一次。

12月 25

给 mysql 数据库,常规来说得能访问 mysql 和 .sql 文件。对于 docker,可以通过映射或copy 的方式,将 .sql 语句弄到容器中,再执行 sql 语句。这种方式虽然可以实现,但感觉还是太麻烦了。还不如在宿主机,直接执行。 docker 提供了 exec 命令,可以用来搞定。

docker exec -i fa7 mysql -u zeipan -pzeipanpwd zeipan < /zeipan/conf/mysql/zeipan.sql

其中

  • -u zeipan 数据库用户名
  • -pzeipanpwd 数据库密码
  • zeipan 数据库库名
  • /zeipan/conf/mysql/zeipan.sql 宿主机的 sql 文件

这种方式虽然可以搞定。但是会报一个下边这样的警告。不让把密码直接弄到终端上。

mysql: [Warning] Using a password on the command line interface can be insecure.
12月 25

先查看

php

<?php
// 获取单个上传文件的最大大小
$uploadMaxFilesize = ini_get('upload_max_filesize');

// 获取 POST 请求的最大大小
$postMaxSize = ini_get('post_max_size');

echo "upload_max_filesize: $uploadMaxFilesize\n";
echo "post_max_size: $postMaxSize\n";
?>

也可以在 phpinfo() 函数中找到。

nginx

nginx -T | grep client_max_body_size

再修改

这里以 docker 来说。 如果没做宿主机配置文件和容器的映射就做一个,如果做了就修改配置文件。

假如这里没做映射。

在宿主机 compose.yaml 文件对应的目录下。新建 ./conf/php/php.ini 文件,加入以下配置。

# 单个上传文件的大小
upload_max_filesize = 50M

# 请求的最大大小
post_max_size = 50M

这里很灵活,只加想加的配置即可。

然后修改 compose.yaml 配置文件。

  php82:
    …
    volumes:
      - ${SOURCE_DIR}:/www/:rw
      - ${CONF_DIR}/php/php.ini/:/usr/local/etc/php/php.ini
      - ${LOG_DIR}/php82:/var/log/php

然后重启 php, 使得配置生效。

# 请记得 php82 是 compose.yaml 文件中,配置的 service 的名字。
docker-compose restart php82

这样就好了。

12月 21

有的时候就是这样,不给你网络,又要安装服务环境。这里是离线安装 docker 环境。传统 windows,下载好 exe 直接安装就好了,这里是 CentOS 7 、CentOS 8,也是类似的。

准备工作

  1. 找个可以联网的机器,下载 Docker RPM 包。

    • 访问 CentOS7 Docker CE Release 页面CentOS8 Docker CE Release 页面
    • 下载一下去 RPM 包(根据你的需求选择对应版本):
    • containerd.io-<VERSION>.rpm
    • docker-ce-<VERSION>.rpm
    • docker-ce-cli-<VERSION>.rpm
    • docker-ce-rootless-extras-<VERSION>.rpm
    • docker-buildx-plugin-<VERSION>.rpm
    • docker-ce-rootless-extras-<VERSION>.rpm
  2. 将下载的 RPM 包传输到离线机器。

  3. 下载 Docker Compose

    • Docker Compose GitHub Releases 。选择适应的版本。我喜欢用最新版,当前是 v2.23.2 点击进去,找到 linux 的版本。这里是 docker-compose-linux-x86_64

进去的链接如: https://github.com/docker/compose/releases/tag/v2.23.2
`

  1. 将下载的 Docker Compose 安装包传输到离线机器。

    安装

在离线机器上安装 RPM 包:

sudo yum install ./containerd.io-<VERSION>.rpm
sudo yum install ./docker-ce-<VERSION>.rpm ./docker-ce-cli-<VERSION>.rpm

启动 docker 服务

sudo systemctl start docker

将 Docker 添加到开机启动:

sudo systemctl enable docker

验证 Docker 安装

docker --version

解压 docker-compose 安装包

# 将 docker-compose 可执行文件移动到 /usr/bin 目录下
mv docker-compose-linux-x86_64 /usr/local/bin/docker-compose

# 赋予执行权限
sudo chmod +x /usr/local/bin/docker-compose

验证 Docker Compose 安装包

docker-compose --version