深度优先

这个家伙好懒,除了文章什么都没留下

0%

原文地址

前言

对于使用Windows操作系统的人来说,Windows Service(Windows服务)应该不会陌生。在Windows操作系统中,我们可以在”运行”窗口中运行service.msc

即可打开一个查看Windows服务的窗口,如图:

Windows服务基本都是一些后台运行的服务进程,没有UI界面,每个服务处理着各自独立的任务并且有专门的启动或者停止策略。所以,Windows服务在很多情况下会被用来者处理一些定时任务或者调度。

那么,对于.NET的开发者来说,可不可以自己创建Windows服务呢,如何使用C#创建Windows服务呢?

本文就为大家分享一种基于Topshelf创建的Windows服务的方法。

创建Topshelf服务项目

首先打开Visual Studio(本文使用的是Visual Studio 2019),打开新建项目的对话框,选择.NET Framework的控制台应用程序(Console App(.NET Framework)),如图:

注:只可选择控制台应用程序

点击”下一步”,在项目名称中输入TopshelfDemoService,.NET Framework 选择4.6.2,其中选项根据自己情况填写即可,最后点击”创建”按钮。

安装Topshelf组件

TopshelfDemoService项目中,打开Nuget包管理工具,搜索Topshelf,在搜索结果中选中Topshelf,点击”安装”,如图:

编写Topshelf服务的示例程序代码

Topshelf组件安装完成后,我们就可以开始编写服务的示例代码了。

首先,创建一个名为HealthMonitorService.cs的类(其作用假设为定时监控某个系统的运行健康状况),在其中分别创建方法:Start()Stop()以及一个定时器,让定时器定时执行检查系统健康状况的任务(这里模拟的每秒向控制台输出一条文本信息),完整的代码如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
using System;
using System.Timers;

namespace TopshelfDemoService
{
internal class HealthMonitorService
{
private readonly Timer _timer;
public HealthMonitorService()
{
_timer = new Timer(1000) { AutoReset = true };
_timer.Elapsed += (sender, eventArgs) => Console.WriteLine("执行系统健康检查任务,所有指标均正常。执行时间:{0}", DateTime.Now);
}

public void Start()
{
_timer.Start();
}
public void Stop()
{
_timer.Stop();
}
}
}

再创建一个名为MyServiceConfigure.cs的服务配置类,这个类主要用来配置Topshelf服务的各种运行参数,代码如下:

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
32
33
34
35
using System;
using Topshelf;

namespace TopshelfDemoService
{
internal class MyServiceConfigure
{
internal static void Configure()
{
var rc = HostFactory.Run(host => // 1
{
host.Service<HealthMonitorService>(service => // 2
{
service.ConstructUsing(() => new HealthMonitorService()); // 3
service.WhenStarted(s => s.Start()); // 4
service.WhenStopped(s => s.Stop()); // 5
});

host.RunAsLocalSystem(); // 6

host.EnableServiceRecovery(service => // 7
{
service.RestartService(3); // 8
});
host.SetDescription("Windows service based on topshelf"); // 9
host.SetDisplayName("Topshelf demo service"); // 10
host.SetServiceName("TopshelfDemoService"); // 11
host.StartAutomaticallyDelayed(); // 12
});

var exitCode = (int)Convert.ChangeType(rc, rc.GetTypeCode()); // 13
Environment.ExitCode = exitCode;
}
}
}

注:其中数字的含义请见本文末尾的解释。

最后,打开Program.cs文件,开启Topshelf服务,如下:

1
2
3
4
5
6
7
8
9
10
namespace TopshelfDemoService
{
class Program
{
static void Main(string[] args)
{
MyServiceConfigure.Configure();
}
}
}

好了,完成到这里,整个示例程序就写好了,按F5运行示例程序,你将看到如下类似的控制台信息:

可以看到,我们创建的TopshelfDemoService服务每秒向控制台打印了一条文本信息,这和我们的预期是吻合的。

这样,我们就成功创建了一个基于Topshelf的Windows服务,当然,这也只是一个简单和示例服务程序,其中没有复杂的业务逻辑和配置等等。这些都等待你去发掘。

作为Windows服务安装和卸载

我们刚才运行的只是一个控制台应用程序,如果将这个控制台应用程序关掉,定时任务也会被停止了。如果我们希望定时任务可以一直运行,那需要将这个控制台应用程序作为服务安装到Windows服务进程中,如何操作呢?

非常简单的安装和卸载命令。

首先,以管理员身份打开一个命令行工具,进入到控制台应用程序所在目录。

安装

安装服务运行如下命令:

1
TopshelfDemoService.exe install

打开Windows服务查看窗口(刷新),可以看到Topshelf demo service已经在服务列表中了,如图:

这时,我们只需要按照Windows服务来操作这个服务即可。

卸载

如果需要卸载服务,则运行如下命令:

1
TopshelfDemoService.exe uninstall

Topshelf配置参数说明

1.设置服务主机使用HostFactory.Run()来创建并运行一个Topshelft服务。
2.设置 Topshelf使用类型HealthMonitorService作为服务类。
3.配置如何创建一个服务的实例,这里采用的是使用关键字 new来实例化一个HealthMonitorService对象,你也可以使用IoCp容器来实例化服务对象。
4.设置当服务启动时执行的操作。
5.设置当服务停止时执行的操作。
6.设置将服务以本地系统身份运行。
7.启动恢复服务模式(当服务意外停止后自动恢复)。
8.设置第一次自动恢复服务的延迟时间为3分钟。
9.设置Topshelf服务在Windows服务中的描述信息。
10.设置Topshelf服务在Windows服务中的显示名称。
11.设置Topshelf服务在Windows服务中的服务名称。
12.设置Topshelf服务随Windows启动时自动运行(延迟)。
13.设置服务的退出代码。

示例代码托管和下载

本示例代码托管地址可以在原出处找到:示例代码下载地址

https://www.cnblogs.com/wq3435/p/6516024.html

备份容器

首先,为了备份Docker中的容器,我们会想看看我们想要备份的容器列表。要达成该目的,我们需要在我们运行着Docker引擎,并已创建了容器的Linux机器中运行 docker ps 命令。

docker ps

在此之后,我们要选择我们想要备份的容器,然后去创建该容器的快照。我们可以使用 docker commit 命令来创建快照。

docker commit -p 30b8f18f20b4 container-backup

该命令会生成一个作为Docker镜像的容器快照,我们可以通过运行 docker images 命令来查看Docker镜像,如下。

docker images

正如我们所看见的,上面做的快照已经作为Docker镜像保存了。现在,为了备份该快照,我们有两个选择,一个是我们可以登录进Docker注册中心,并推送该镜像;另一个是我们可以将Docker镜像打包成tar包备份,以供今后使用。

如果我们想要在Docker注册中心上传或备份镜像,我们只需要运行 docker login 命令来登录进Docker注册中心,然后推送所需的镜像即可。

docker login

1
2
docker tag a25ddfec4d2a arunpyasi/container-backup:test
docker push arunpyasi/container-backup

如果我们不想备份到docker注册中心,而是想要将此镜像保存在本地机器中,以供日后使用,那么我们可以将其作为tar包备份。要完成该操作,我们需要运行以下 docker save 命令。

docker save -o ~/container-backup.tar container-backup

要验证tar包是否已经生成,我们只需要在保存tar包的目录中运行 ls 命令即可。

恢复容器

接下来,在我们成功备份了我们的Docker容器后,我们现在来恢复这些制作了Docker镜像快照的容器。如果我们已经在注册中心推送了这些Docker镜像,那么我们仅仅需要把那个Docker镜像拖回并直接运行即可。

docker pull arunpyasi/container-backup:test

但是,如果我们将这些Docker镜像作为tar包文件备份到了本地,那么我们只要使用 docker load 命令,后面加上tar包的备份路径,就可以加载该Docker镜像了。

docker load -i ~/container-backup.tar

现在,为了确保这些Docker镜像已经加载成功,我们来运行 docker images 命令。

docker images

在镜像被加载后,我们将用加载的镜像去运行Docker容器。

docker run -d -p 80:80 container-backup

迁移Docker容器

迁移容器同时涉及到了上面两个操作,备份和恢复。我们可以将任何一个Docker容器从一台机器迁移到另一台机器。在迁移过程中,首先我们将把容器 备份为Docker镜像快照。然后,该Docker镜像或者是被推送到了Docker注册中心,或者被作为tar包文件保存到了本地。如果我们将镜像推送 到了Docker注册中心,我们简单地从任何我们想要的机器上使用 docker run 命令来恢复并运行该容器。但是,如果我们将镜像打包成tar包备份到了本地,我们只需要拷贝或移动该镜像到我们想要的机器上,加载该镜像并运行需要的容器 即可。

注:可搭配这篇食用

原文地址:https://www.linuxea.com/2270.html

如果有这样以一个场景。我使用docker run启动了一个redis容器,而docker run的参数有很多,由于时间太久,我已经忘记了当初的启动参数,也并没有使用 docker-compose这样的编排工具。现在我如何进行重启,如何找回此前的启动命令?

  • 示例

使用docker run启动一个容器:

1
2
3
4
5
6
7
8
9
10
11
12
13
docker run \
--detach \
--env REDIS_CONF=on \
--env REQUIRE_PASS=OTdmOWI4ZTM4NTY1M2M4OTZh \
--env MASTER_AUTH=OTdmOWI4ZTM4NTY1M2M4OTZh \
--env MAXCLIENTS_NUM=30 \
--net=host \
--env MAXMEMORY_SIZE=1024M \
--publish 6379:6379 \
--volume linuxea_volume \
--name linuxea_redis \
--tty --interactive \
marksugar/redis:5.0.0

docker命令查看

首先,我们查看启动的脚本参数和完整的命令

使用 docker ps -a --no-trunc

docker ps -a --no-trunc可以打印docker ps -a的隐藏部分,但是这并不能看到我们run的参数

1
2
3
4
[root@LinuxEA-172_25_50_250 ~]# docker ps -a --no-trunc
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
c32bc1bba3a0f8d59e3bd96648838399287ee183ddbff1ca1293ccbc933c8ede marksugar/redis:5.0.0 "/Initialization.sh" 8 minutes ago Up 8 minutes linuxea_redis
1d848a10149325e6fb46ef5fa0624812ab12f28263535720d79aa564a5551e45 marksugar/nginx_createrepo:v0.2 "entrypoint.sh" 13 days ago Up 13 days nginx_repo

docker inspect

此时,我们试图找回上述的docker run命令,使用docker inspect ,但是docker inspect打印的是json文件,我们需要挑选字段查看

1
2
3
[root@LinuxEA-172_25_50_250 ~]# docker inspect -f "{{.Name}} {{.Path}} {{.Args}} {{.Config.Cmd}} {{.Config.Env}} {{.Config.Volumes}} {{.Config.Entrypoint}} {{.NetworkSettings.Networks}}" $(docker ps -a -q)
/linuxea_redis /Initialization.sh [] [] [REDIS_CONF=on REQUIRE_PASS=OTdmOWI4ZTM4NTY1M2M4OTZh MASTER_AUTH=OTdmOWI4ZTM4NTY1M2M4OTZh MAXCLIENTS_NUM=30 MAXMEMORY_SIZE=1024M PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin RS_VSON=5.0.0 RS_USER=redis RS_VSON_URL=http://download.redis.io/releases/redis-5.0.0.tar.gz BATADIR=/usr/local/redis DATADIR=/data/redis DATALOG=/data/logs DATACIG=/etc/redis] map[linuxea_volume:{}] [/Initialization.sh] map[host:0xc420182000]
/nginx_repo entrypoint.sh [] [] [USERNAME=marksugar FTPPASSWD=123 FTPDATA=/data/wwwroot SERVER_NAME=meftp.ds.com NGINX_PORT=80 WELCOME="welome to linuxea.com" PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin version=1.14.2 USER=www INSTALL_PATH=/usr/local WWWPATH=/data/wwwroot] map[/tmp/header.html:{} /data:{} /etc/localtime:{} /etc/nginx/nginx.conf:{} /etc/nginx/vhost:{} /tmp/footer.html:{}] [entrypoint.sh] map[host:0xc42044c000]

这样的显示并不直观。好在我发现了rekcodrunlike这两个逆转docker run命令,参见本章介绍和使用。runlike更漂亮些

rekcod

借助rekcod打印出更易读的格式,docker inspectdocker run

docker run从现有容器(via docker inspect)反向设计命令。

rekcod可以将以下任何内容转换为docker run命令:

  1. 容器ID /名称(rekcod将调用docker inspect
  2. 包含docker inspect输出的文件的路径
  3. 原始JSON(docker inspect直接传递输出)

每个docker run命令都可用于复制容器。

这不是非常强大,但它应该涵盖所需的大多数参数。请参阅下面支持的字段

传递容器ID /名称时,此模块docker inspect直接调用,运行它的用户也应该能够。

开始安装

  • install rekcod
1
2
yum install npm -y
npm i -g rekcod
  • 如果您只安装了Docker:
1
2
$ docker pull nexdrew/rekcod
$ alias rekcod="docker run --rm -v /var/run/docker.sock:/var/run/docker.sock nexdrew/rekcod"

或者你可以简单地运行它,不需要安装:

1
$ docker run --rm -v /var/run/docker.sock:/var/run/docker.sock nexdrew/rekcod <container>

开始使用

以使用linuxea_redis容器为例,rekcod linuxea_redis,如下:

1
[root@LinuxEA-172_25_50_250 ~]# docker ps -qa|rekcod

或者这样

1
2
3
[root@LinuxEA-172_25_50_250 ~]#  rekcod linuxea_redis

docker run --name linuxea_redis --runtime runc -p 6379:6379/tcp --net host --restart no -h LinuxEA-Node172_25_50_250.cluster.com --expose 26379/tcp --expose 6379/tcp -e 'REDIS_CONF=on' -e 'REQUIRE_PASS=OTdmOWI4ZTM4NTY1M2M4OTZh' -e 'MASTER_AUTH=OTdmOWI4ZTM4NTY1M2M4OTZh' -e 'MAXCLIENTS_NUM=30' -e 'MAXMEMORY_SIZE=1024M' -e 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' -e 'RS_VSON=5.0.0' -e 'RS_USER=redis' -e 'RS_VSON_URL=http://download.redis.io/releases/redis-5.0.0.tar.gz' -e 'BATADIR=/usr/local/redis' -e 'DATADIR=/data/redis' -e 'DATALOG=/data/logs' -e 'DATACIG=/etc/redis' -d -t -i --entrypoint "/Initialization.sh" marksugar/redis:5.0.0

我将结果换行查看,并且进行启动 这里不得不说的是,在runlike中,可以使用-q自动换行.见下文

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
docker run --name linuxea_redis \
-p 6379:6379/tcp \
--net host \
--restart no \
-h LinuxEA-Node172_25_50_250.cluster.com \
--expose 26379/tcp \
--expose 6379/tcp \
-e 'REDIS_CONF=on' \
-e 'REQUIRE_PASS=OTdmOWI4ZTM4NTY1M2M4OTZh' \
-e 'MASTER_AUTH=OTdmOWI4ZTM4NTY1M2M4OTZh' \
-e 'MAXCLIENTS_NUM=30' \
-e 'MAXMEMORY_SIZE=1024M' \
-e 'PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin' \
-e 'RS_VSON=5.0.0' \
-e 'RS_USER=redis' \
-e 'RS_VSON_URL=http://download.redis.io/releases/redis-5.0.0.tar.gz' \
-e 'BATADIR=/usr/local/redis' \
-e 'DATADIR=/data/redis' \
-e 'DATALOG=/data/logs' \
-e 'DATACIG=/etc/redis' \
-d -t -i --entrypoint "/Initialization.sh" marksugar/redis:5.0.0

启动成功

1
2
3
4
[root@LinuxEA-172_25_50_250 ~]# docker ps -a
CONTAINER ID IMAGE COMMAND CREATED STATUS PORTS NAMES
492f6cdc159c marksugar/redis:5.0.0 "/Initialization.sh" 4 seconds ago Up 3 seconds linuxea_redis
1d848a101493 marksugar/nginx_createrepo:v0.2 "entrypoint.sh" 13 days ago Up 13 days nginx_repo
1
2
3
4
5
6
7
8
9
[root@LinuxEA-172_25_50_250 ~]# docker logs linuxea_redis
[i] Start configuration /etc/redis
[ok] /etc/redis/redis.conf config ready
[ok] Start variable substitution REDIS_CONF=on
[ok] Replace the maxclients=30 variable value
[ok] Replace the maxmemory=1024M variable value
[ok] Replace the requirepass=OTdmOWI4ZTM4NTY1M2M4OTZh variable value
[ok] Replace the masterauth=OTdmOWI4ZTM4NTY1M2M4OTZh variable value
[i] Start up /usr/local/bin/redis-server /etc/redis/redis.conf

runlike

我仍然要提一下runlike,和rekcod有一样的作用

本地安装使用

1
yum install python-pip
1
pip install runlike
  • 用法:
1
runlike <container-name>

这打印出你需要运行什么来获得类似的容器。你可以$(runlike container-name)一步完成其输出。如下:

1
2
[root@LinuxEA-172_25_50_250 ~]# runlike linuxea_redis
docker run --name=linuxea_redis --hostname=LinuxEA-Node172_25_50_250.cluster.com --env=REDIS_CONF=on --env=REQUIRE_PASS=OTdmOWI4ZTM4NTY1M2M4OTZh --env=MASTER_AUTH=OTdmOWI4ZTM4NTY1M2M4OTZh --env=MAXCLIENTS_NUM=30 --env=MAXMEMORY_SIZE=1024M --env=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin --env=RS_VSON=5.0.0 --env=RS_USER=redis --env=RS_VSON_URL=http://download.redis.io/releases/redis-5.0.0.tar.gz --env=BATADIR=/usr/local/redis --env=DATADIR=/data/redis --env=DATALOG=/data/logs --env=DATACIG=/etc/redis --volume=linuxea_volume --network=host -p 6379:6379 --restart=no --detach=true -t marksugar/redis:5.0.0

-p将命令行分解为漂亮漂亮的行。例如:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
[root@LinuxEA-172_25_50_250 ~]# runlike -p linuxea_redis
docker run \
--name=linuxea_redis \
--hostname=LinuxEA-Node172_25_50_250.cluster.com \
--env=REDIS_CONF=on \
--env=REQUIRE_PASS=OTdmOWI4ZTM4NTY1M2M4OTZh \
--env=MASTER_AUTH=OTdmOWI4ZTM4NTY1M2M4OTZh \
--env=MAXCLIENTS_NUM=30 \
--env=MAXMEMORY_SIZE=1024M \
--env=PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin \
--env=RS_VSON=5.0.0 \
--env=RS_USER=redis \
--env=RS_VSON_URL=http://download.redis.io/releases/redis-5.0.0.tar.gz \
--env=BATADIR=/usr/local/redis \
--env=DATADIR=/data/redis \
--env=DATALOG=/data/logs \
--env=DATACIG=/etc/redis \
--volume=linuxea_volume \
--network=host \
-p 6379:6379 \
--restart=no \
--detach=true \
-t \
marksugar/redis:5.0.0

docker使用

runlike被封装为一个名为assaflavie / runlike的Docker镜像。

1
2
docker run --rm -v /var/run/docker.sock:/var/run/docker.sock \
assaflavie/runlike YOUR-CONTAINER

或者您可以使用别名运行它,例如,将其保存在~/.profile或中~/.bashrc

1
alias runlike="docker run --rm -v /var/run/docker.sock:/var/run/docker.sock assaflavie/runlike"

然后您可以直接以本地命令运行

1
runlike YOUR-CONTAINER

docker-compose

事实上这样的方式,我并不推荐,你大可不必这样 麻烦,直接使用docker-compose就可以完成简单的编排,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
version: '2'
services:
redis:
image: marksugar/redis:5.0.0
container_name: redis
restart: always
network_mode: "host"
privileged: true
environment:
- REDIS_CONF=on
- REQUIRE_PASS=OTdmOWI4ZTM4NTY1M2M4OTZh
- MASTER_AUTH=OTdmOWI4ZTM4NTY1M2M4OTZh
- MAXCLIENTS_NUM=600
- MAXMEMORY_SIZE=4096M
volumes:
- /etc/localtime:/etc/localtime:ro
# - /etc/redis:/etc/redis
- /data/redis-data:/data/redis:Z
- /data/logs:/data/logs