LIANTIAN's LOG

水底渐干,月渐满...

0%

mplfinance是一个非常方便的单的画图库,基于matplotlib,主要就是用来画k线图。

先根据上一篇文章,将数据提取出来。
#17

1
2
3
4
5
6
7
8
9
# 使用read_frame将django orm query转换为pandas.DataFrame
df = read_frame(stock_hst,fieldnames=['date', 'Open', 'Close',"High","Low","Volume"])
# 有些字段需要改名,才可以配合mplfinance使用,通过字典,可以一次更改多个字段。
df.rename(columns={"date":"Date"}, inplace=True)
# read_frame读到的datetime.Date为字符串,需要使用pandas的方法转换为时间
df['Date'] = pd.to_datetime(df['Date'])
# read_frame会将id作为索引,mplfinance需要将日期设置为索引
df.set_index(["Date"], inplace=True)
df

数据集效果如下
132950439-3f247575-b137-42bf-a269-b6131e5961bf.png

mplfinance要求列名强制为 Open | Close | High | Low | Volume,索引为 Date

均线使用10、20、60日,画一个candle图,candle是主图线条的样式..

1
2
3
4
5
6
7
import matplotlib

import pandas as pd

import mplfinance as mpf

mpf.plot(df, type='candle',mav=(10,20,60), volume=True)

132950550-158a757b-df1d-4764-9d77-277e16cf593b.png

其他样式包括

1
mpf.plot(df, type='ohlc',mav=(10,20,60), volume=True)

132950575-3a17c08d-4aa0-4237-a9ef-29b2a569637a.png

从django-orm读取数据到pandas.DataFrame

1
from django_pandas.io import read_frame

使用django orm读取信息

1
stock_hst = AStockHist.objects.filter(stock_id=code,date__gte=datetime.date(2021, 1, 1)).order_by("date").all()

使用read_frame将django orm query转换为pandas.DataFrame

1
df = read_frame(stock_hst,fieldnames=['date', 'Open', 'Close',"High","Low","Volume"])

有些字段需要改名,才可以配合mplfinance使用,通过字典,可以一次更改多个字段。

1
df.rename(columns={"date":"Date"}, inplace=True)

read_frame读到的datetime.Date为字符串,需要使用pandas的方法转换为时间

1
df['Date'] = pd.to_datetime(df['Date'])

read_frame会将id作为索引,实际往往需要将日期设置为索引

1
df.set_index(["Date"], inplace=True)

将抓取的数据存入django

其实输入数据库的方法很多,只要通过DateFrame.iloc[i]就可以历遍数据集中的行,并写入数据。但是如果使用有读写次数限制的云数据库,下面的操作则能使django-orm使用最少的读写次数 (一次select 一次insert)

通过akshare抓取数据为DataFrame(其他抓取方法均类似)

1
2
import akshare as ak
stock_zh_a_hist_df = ak.stock_zh_a_hist(symbol=stock_id, adjust="hfq")

获取数据库内已有的记录,这里将结尾保存为日期的集合,便于后面求交集、差集、并集。

1
2
3
4
5
6
7
8
9
# 单行写法
in_db_hst = set([t.strftime('%Y-%m-%d') for t in AStockHist.objects.filter(stock=stock).values_list('date', flat=True)])

# 多行写法
qs = AStockHist.objects.filter(stock=stock).values_list('date', flat=True)
in_db_hst = []
for q in qs:
in_db_hst.append(q.strftime('%Y-%m-%d'))
in_db_hst = set(in_db_hst)

获取DateFrame里的日期集合

1
cur_hst = set(stock_zh_a_hist_df["日期"].values.tolist())

求差集,得到数据库内没有,需要更新的日期,并根据这个集合过滤此前的DateFrame

1
2
set_need_create_hst = cur_hst - in_db_hst
need_create_hst = stock_zh_a_hist_df[stock_zh_a_hist_df['日期'].isin(set_need_create_hst)]

批量写入数据库。

1
2
3
4
5
6
7
8
9
bulk_create_list = []
for i in range(0, need_create_hst.index.__len__()):
hst = need_create_hst.iloc[i]
new_hst = AStockHist(stock = stock,date = datetime.strptime(hst['日期'], '%Y-%m-%d').date())
new_hst.Open = hst['开盘']
......
......
bulk_create_list.append(new_hst)
AStockHist.objects.bulk_create(bulk_create_list)

在Jupyter Notebook中访问django

  • 安装扩展 django-extensions
    1
    2
    @echo off
    cmd /k "CHCP 65001 & cd /d %~dp0\.env\Scripts & activate & cd /d %~dp0 & python manage.py shell_plus --notebook & exit 0"

数据采集

1. 通过公开的数据接口抓取信息,自己造爬虫轮子

这些接口并不会经常变化,并且官方似乎也没想过通过Token等手段禁止爬虫。但是实际上使用起来比较困难。
记得以前有新浪大佬讲:”加个token认证更费cpu,实际爬虫的影响不到1/1000。“

2. 包装好的开源库

三者的开源版本都是通过requests、pandas、numpy等库,对腾讯、新浪、东财接口的再封装,使用便捷。
其中Tushare包含收费版本。

免费的缺点就是缺乏实时数据、数据有反爬虫的频次限制、没有历史财务指标数据。

3. 一些小厂的实时数据

  • 好灵数据
    网址:h0.cn

类似的可能还有很多,但是我没用过,这些数据大多需要通过自写爬虫,配合api-key使用。

优点:收费较低。
缺点:稳定性较差,没有成熟的封装好的苦。

4. 大厂数据

  • 万得资讯
  • 同花顺
  • 等等….

优点:往往包含历史财务数据,实时数据到L2级别,同时都有完善的接口,适配java/python等。
缺点:贵

PS. 如果在金融企业,已购买万得的情况下,可以联系客户经理,申请试用账号用于研发。

环境准备

  • 64位的OS和Python
    64位并不是强制要求,但是强烈推荐,因为部分第三方库缺少32位的预编译版本。比如py-mini-racer

  • Anaconda
    Anaconda是一个用于科学计算的Python发行版,对于不数据python第三方库的安装和编译的同学,这是最简单的选择。

    • 使用清华Anaconda源是改善国内anaconda使用速度的方法。
    • 对于Python老手,不使用anaconda也是个好主意。因为conda和pip还是会产生一些冲突。导致conda update失败。
  • conda-forge
    conda-forge是anaconda的社区版本,anaconda存在一些商业授权问题,conda-forge则规避了这些问题。适合商业环境下使用,并且conda-forge的库更丰富。
    安装conda-forge的好办法是使用MiniForge,并修改.condarc文件如下使用清华源。

1
2
channels: [https://mirrors.tuna.tsinghua.edu.cn/anaconda/cloud/conda-forge/]
show_channel_urls: true
  • 安装必要的第三方库
    不管使用原版Python,还是Anaconda,或conda-forge,都需要这些库:

    • ipython
    • jupyter
    • notebook
    • matplotlib
    • numpy
    • Pillow
    • mplfinance

    另外还需要:

    • 自己习惯的数据库访问库,或者把数据都保存成csv
    • 自己习惯的orm

    如果喜欢使用Django,那么还需要:

    • django-extensions
    • django-pandas

1
2
3
4
5
6
7
8
9
FROM alpine:latest

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories && \
apk update && \
apk upgrade && \
apk add --no-cache bash bash-doc bash-completion

CMD ["/bin/bash"]

1
2
3
4
5
6


buildah bud -t alpine-bash:latest .


podman run --rm -it alpine-bash

先登陆到RH

1
2
3
4
5
6
# subscription-manager register
Registering to: subscription.rhsm.redhat.com:443/subscription
Username: ********
Password: **********

# podman login registry.redhat.io

新建数据库运行路径,并设置权限

1
2
3
mkdir -p /data/mariadb/datebase
mkdir -p /data/mariadb/log
chown -R 27:27 /data/mariadb/

制作一个Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
FROM registry.redhat.io/ubi8/ubi
RUN yum -y install bash-completion nano net-tools iputils && \
yum -y module enable mariadb:10.3 && \
yum -y module install mariadb && \
yum clean all
RUN usermod -a -G root mysql && \
mkdir -p /var/lib/mysql && \
chown -R mysql:mysql /var/lib/mysql && \
chmod 777 /var/lib/mysql

USER 27
VOLUME ["/var/lib/mysql","/var/log/mariadb/"]
EXPOSE 3306
STOPSIGNAL SIGINT

CMD ["/usr/libexec/mysqld","--basedir=/usr","--datadir=/var/lib/mysql","--plugin-dir=/usr/lib64/mariadb/plugin","--log-error=/var/log/mariadb/mariadb.log"]

使用buildah构建image

1
buildah bud -t mariadb:$(date +%Y%m%d%H%M%S) .

初始化数据库(以后升级不再需要)

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
podman run --rm -it \
-p 3306:3306 \
-v /data/mariadb/log:/var/log/mariadb \
-v /data/mariadb/datebase:/var/lib/mysql \
-u=root \
localhost/mariadb:20210627221353 \
/bin/bash


# 进入容器后执行
mysql_install_db
chown -R mysql:mysql /var/lib/mysql
mysqld_safe
/usr/bin/mysql_secure_installation

新建并运行容器

1
2
3
4
5
6
7
8
9
10
podman create  \
-p 3306:3306 \
-v /data/mariadb/log:/var/log/mariadb \
-v /data/mariadb/datebase:/var/lib/mysql \
--name=MariaDB \
localhost/mariadb:20210627221353


podman start MariaDB

建立数据库开通远程访问

1
2
3
4
5
6
7
8
9
podman exec -it MariaDB bash
mysql -u root -p

CREATE DATABASE db_name DEFAULT CHARACTER SET utf8 COLLATE utf8_unicode_ci;
CREATE OR REPLACE USER db_user IDENTIFIED BY 'password';

GRANT ALL PRIVILEGES ON db_name.* TO 'db_user'@'远程ip' IDENTIFIED BY 'password' WITH GRANT OPTION;
FLUSH PRIVILEGES;

先登陆到RH

1
2
3
4
5
6
# subscription-manager register
Registering to: subscription.rhsm.redhat.com:443/subscription
Username: ********
Password: **********

# podman login registry.redhat.io

新建数据库运行路径,并设置权限

1
2
3
mkdir -p /data/psql/datebase
mkdir -p /data/psql/log
chown -R 26:26 /data/psql/

制作一个Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
FROM registry.redhat.io/ubi8/ubi
RUN yum -y install bash-completion nano net-tools iputils && \
yum -y module enable postgresql:12 && \
yum -y module install postgresql && \
yum clean all
RUN usermod -a -G root postgres && \
mkdir -p /var/lib/pgsql/data && \
chown -R postgres:postgres /var/lib/pgsql/data && \
chmod 777 /var/lib/pgsql/data

USER 26
VOLUME ["/var/lib/pgsql/data"]
EXPOSE 5432
STOPSIGNAL SIGINT

CMD ["postgres","-D","/var/lib/pgsql/data"]

使用buildah构建image

1
buildah bud -t psql:$(date +%Y%m%d%H%M%S) .

初始化数据库(以后升级不再需要)

1
2
3
4
5
6
7
8
9
10
podman run --rm -it \
-p 5432:5432 \
-v /data/psql/log:/var/log \
-v /data/psql/datebase:/var/lib/pgsql/data \
localhost/psql:20210627192640 \
/bin/bash


# 进入容器后执行
initdb -D /var/lib/pgsql/data

新建并运行容器

1
2
3
4
5
6
7
8
9
10
podman create  \
-p 5432:5432 \
-v /data/psql/log:/var/log \
-v /data/psql/datebase:/var/lib/pgsql/data \
--name=PostgreSQL \
localhost/psql:20210627192640


podman start PostgreSQL

去掉采集器只能在赤道上搭建的限制

BuildTool_Click.cs

1
2
3
4
5
6
if (y2 > 0.1f || y2 < -0.1f)
{
// 注销掉
// buildPreview.condition = EBuildCondition.BuildInEquator;
goto IL_1CF7;
}

去掉抽水机的限制

BuildTool_Click.cs

1
2
3
//buildPreview.condition = EBuildCondition.NeedWater;
替换为
buildPreview.condition = EBuildCondition.Ok;

0.7.18的连续建造限制

BuildTool_Click.cs

1
2
3
4
protected override void _OnInit()
{
this.dotsSnapped = new Vector3[12]; // 12可以改大
}

让宇宙密度更大,星系间路径更小

UniverseGen.cs

1
2
3
4
num = UniverseGen.GenerateTempPoses(dotNet35Random.Next(), num, 4, 2.0, 2.3, 3.5, 0.18);
// 替换为
num = UniverseGen.GenerateTempPoses(dotNet35Random.Next(), num, 4, 0.6, 0.8, 1.6, 0.18);

不需要沙土

PlanetFactory.cs

1
2
3
4
修改 ComputeFlattenTerrainReform的结尾
return num7;
修改为
return -Mathf.Abs(num7);

星球数量

StarGen.cs

找到CreateStarPlanets,从第一个if开始到star.planetCount = 1;

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
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
152
153
154
155
156
157
158
159
160
161
162
163
164
165
166
167
168
169
if (star.type == EStarType.BlackHole)
{
star.planetCount = 1;
star.planets = new PlanetData[star.planetCount];
int info_seed = dotNet35Random2.Next();
int gen_seed = dotNet35Random2.Next();
star.planets[0] = PlanetGen.CreatePlanet(galaxy, star, gameDesc, 0, 0, 3, 1, false, info_seed, gen_seed);
}
else if (star.type == EStarType.NeutronStar)
{
star.planetCount = 1;
star.planets = new PlanetData[star.planetCount];
int info_seed2 = dotNet35Random2.Next();
int gen_seed2 = dotNet35Random2.Next();
star.planets[0] = PlanetGen.CreatePlanet(galaxy, star, gameDesc, 0, 0, 3, 1, false, info_seed2, gen_seed2);
}
else if (star.type == EStarType.WhiteDwarf)
{
star.planetCount = 2;
star.planets = new PlanetData[star.planetCount];
if (num2 < 0.30000001192092896)
{
int info_seed3 = dotNet35Random2.Next();
int gen_seed3 = dotNet35Random2.Next();
star.planets[0] = PlanetGen.CreatePlanet(galaxy, star, gameDesc, 0, 0, 3, 1, false, info_seed3, gen_seed3);
info_seed3 = dotNet35Random2.Next();
gen_seed3 = dotNet35Random2.Next();
star.planets[1] = PlanetGen.CreatePlanet(galaxy, star, gameDesc, 1, 0, 4, 2, false, info_seed3, gen_seed3);
}
else
{
int info_seed4 = dotNet35Random2.Next();
int gen_seed4 = dotNet35Random2.Next();
star.planets[0] = PlanetGen.CreatePlanet(galaxy, star, gameDesc, 0, 0, 4, 1, true, info_seed4, gen_seed4);
info_seed4 = dotNet35Random2.Next();
gen_seed4 = dotNet35Random2.Next();
star.planets[1] = PlanetGen.CreatePlanet(galaxy, star, gameDesc, 1, 1, 1, 1, false, info_seed4, gen_seed4);
}
}
else if (star.type == EStarType.GiantStar)
{
star.planetCount = 3;
star.planets = new PlanetData[star.planetCount];
if (num2 < 0.15000000596046448)
{
int info_seed5 = dotNet35Random2.Next();
int gen_seed5 = dotNet35Random2.Next();
star.planets[0] = PlanetGen.CreatePlanet(galaxy, star, gameDesc, 0, 0, (num3 > 0.5) ? 3 : 2, 1, false, info_seed5, gen_seed5);
info_seed5 = dotNet35Random2.Next();
gen_seed5 = dotNet35Random2.Next();
star.planets[1] = PlanetGen.CreatePlanet(galaxy, star, gameDesc, 1, 0, (num3 > 0.5) ? 4 : 3, 2, false, info_seed5, gen_seed5);
info_seed5 = dotNet35Random2.Next();
gen_seed5 = dotNet35Random2.Next();
star.planets[2] = PlanetGen.CreatePlanet(galaxy, star, gameDesc, 2, 0, (num3 > 0.5) ? 5 : 4, 3, false, info_seed5, gen_seed5);
}
else if (num2 < 0.75)
{
int info_seed6 = dotNet35Random2.Next();
int gen_seed6 = dotNet35Random2.Next();
star.planets[0] = PlanetGen.CreatePlanet(galaxy, star, gameDesc, 0, 0, (num3 > 0.5) ? 3 : 2, 1, false, info_seed6, gen_seed6);
info_seed6 = dotNet35Random2.Next();
gen_seed6 = dotNet35Random2.Next();
star.planets[1] = PlanetGen.CreatePlanet(galaxy, star, gameDesc, 1, 0, 4, 2, true, info_seed6, gen_seed6);
info_seed6 = dotNet35Random2.Next();
gen_seed6 = dotNet35Random2.Next();
star.planets[2] = PlanetGen.CreatePlanet(galaxy, star, gameDesc, 2, 2, 1, 1, false, info_seed6, gen_seed6);
}
else
{
int info_seed7 = dotNet35Random2.Next();
int gen_seed7 = dotNet35Random2.Next();
star.planets[0] = PlanetGen.CreatePlanet(galaxy, star, gameDesc, 0, 0, (num3 > 0.5) ? 4 : 3, 1, true, info_seed7, gen_seed7);
info_seed7 = dotNet35Random2.Next();
gen_seed7 = dotNet35Random2.Next();
star.planets[1] = PlanetGen.CreatePlanet(galaxy, star, gameDesc, 1, 1, 1, 1, false, info_seed7, gen_seed7);
info_seed7 = dotNet35Random2.Next();
gen_seed7 = dotNet35Random2.Next();
star.planets[2] = PlanetGen.CreatePlanet(galaxy, star, gameDesc, 2, 1, 2, 2, false, info_seed7, gen_seed7);
}
}
else
{
Array.Clear(StarGen.pGas, 0, StarGen.pGas.Length);
if (star.index == 0)
{
star.planetCount = 4;
StarGen.pGas[0] = 0.0;
StarGen.pGas[1] = 0.0;
StarGen.pGas[2] = 0.0;
}
else if (star.spectr == ESpectrType.M)
{
star.planetCount = 8;
StarGen.pGas[0] = 0.0;
StarGen.pGas[1] = 0.2;
StarGen.pGas[2] = 0.3;
StarGen.pGas[3] = 0.0;
StarGen.pGas[4] = 0.2;
StarGen.pGas[5] = 0.3;
}
else if (star.spectr == ESpectrType.K)
{
star.planetCount = 8;
StarGen.pGas[0] = 0.0;
StarGen.pGas[1] = 0.18;
StarGen.pGas[2] = 0.28;
StarGen.pGas[3] = 0.28;
StarGen.pGas[4] = 0.0;
StarGen.pGas[5] = 0.18;
StarGen.pGas[6] = 0.28;
StarGen.pGas[7] = 0.28;
}
else if (star.spectr == ESpectrType.G)
{
star.planetCount = 8;
StarGen.pGas[0] = 0.0;
StarGen.pGas[1] = 0.2;
StarGen.pGas[2] = 0.3;
StarGen.pGas[3] = 0.3;
StarGen.pGas[4] = 0.0;
StarGen.pGas[5] = 0.2;
}
else if (star.spectr == ESpectrType.F)
{
star.planetCount = 8;
StarGen.pGas[0] = 0.0;
StarGen.pGas[1] = 0.22;
StarGen.pGas[2] = 0.31;
StarGen.pGas[3] = 0.31;
StarGen.pGas[4] = 0.0;
StarGen.pGas[5] = 0.22;
}
else if (star.spectr == ESpectrType.A)
{
star.planetCount = 8;
StarGen.pGas[0] = 0.1;
StarGen.pGas[1] = 0.28;
StarGen.pGas[2] = 0.3;
StarGen.pGas[3] = 0.35;
StarGen.pGas[4] = 0.1;
StarGen.pGas[5] = 0.28;
}
else if (star.spectr == ESpectrType.B)
{
star.planetCount = 8;
StarGen.pGas[0] = 0.1;
StarGen.pGas[1] = 0.22;
StarGen.pGas[2] = 0.28;
StarGen.pGas[3] = 0.35;
StarGen.pGas[4] = 0.35;
StarGen.pGas[5] = 0.1;
StarGen.pGas[6] = 0.22;
}
else if (star.spectr == ESpectrType.O)
{
star.planetCount = 8;
StarGen.pGas[0] = 0.1;
StarGen.pGas[1] = 0.2;
StarGen.pGas[2] = 0.25;
StarGen.pGas[3] = 0.3;
StarGen.pGas[4] = 0.32;
StarGen.pGas[5] = 0.35;
StarGen.pGas[6] = 0.1;
StarGen.pGas[7] = 0.2;
}
else
{
star.planetCount = 1;
}

修改物流塔上限

UIStationStorage.cs

1
2
3
4
this.maxValueText.text = stationStore.max.ToString();
int num = 100000; // 这里
int num2 = num / 100;

更多的矿物

PlanetAlgorithm.cs

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
if (spectr == ESpectrType.M)
{
p = 2.5f;
}
else if (spectr == ESpectrType.K)
{
p = 2.5f;
}
else if (spectr == ESpectrType.G)
{
p = 2.5f;
}
else if (spectr == ESpectrType.F)
{
p = 2.5f;
}
else if (spectr == ESpectrType.A)
{
p = 2.5f;
}
else if (spectr == ESpectrType.B)
{
p = 2.5f;
}
else if (spectr == ESpectrType.O)
{
p = 2.5f;

安装:

yum module install -y container-tools

登录RH账号:

podman login registry.redhat.io

拉取最小镜像:

podman pull registry.redhat.io/ubi8/ubi-minimal

运行一个叫mybash的容器,启动bash

podman run --name=mybash -it registry.redhat.io/ubi8/ubi-minimal /bin/bash

再次启动mybash

podman start -ai mybash

制作一个开发环境,新建Dockerfile

FROM registry.redhat.io/ubi8/ubi-init

RUN dnf install bash-completion nano  --nodocs
RUN rm -f /etc/localtime
RUN cp  /usr/share/zoneinfo/Asia/Shanghai /etc/localtime



############################################################
# 安装 SSH
############################################################

RUN dnf install openssh-server passwd --nodocs
RUN ssh-keygen -t dsa -P "" -f /etc/ssh/ssh_host_dsa_key  <<< y
RUN ssh-keygen -t rsa -P "" -f /etc/ssh/ssh_host_rsa_key   <<< y
RUN ssh-keygen -t ecdsa -P "" -f /etc/ssh/ssh_host_ecdsa_key   <<< y
RUN ssh-keygen -t ed25519 -P "" -f /etc/ssh/ssh_host_ed25519_key   <<< y
RUN echo "root:root" | chpasswd
RUN systemctl enable sshd


############################################################
# 安装 Python , 同时安装pip,设置为清华源。
############################################################

RUN dnf install -y python38 python38-psycopg2 python38-PyMySQL python38-jinja2 python38-numpy python38-requests python38-scipy python38-setuptools python38-pip
RUN dnf clean all
RUN pip3 config set global.index-url https://pypi.tuna.tsinghua.edu.cn/simple
RUN pip3 install --no-cache-dir django django-debug-toolbar django-rq django-redis 

EXPOSE 22 8000 8080 

启动这个开发环境

buildah bud -t devenv .
podman run    -p 8080:8080 -p 8000:8000 -p 31022:22 -name DevENV localhost/devenv

今天学习到的奇怪的知识点:

  • export/import 丢失的信息比较多:env entrypoint cmd全会丢
  • cmd和entrypoint的区别。cmd设计被用来容易覆盖。entrypoint不容易。

开搞
下载官方镜像,打包tar,清理

1
2
3
docker create --name some-postgres -e POSTGRES_PASSWORD=mysecretpassword  postgres:9.6-alpine
docker export some-postgres > postgres.export.9.6.17.tar
docker container rm some-postgres

回到无网生产机..
创建docker时的密码是可以修改的。修改后这个环境变量就没用了。

1
2
3
4
5
6
7
8
9
10
11
12
13
14
docker import postgres.export.9.6.17.tar  postgres:9.6.17
sudo mkdir -p /DATA/database/psql9.6/

docker create \
--name some-postgres \
-e LANG=en_US.utf8 \
-e PGDATA=/var/lib/postgresql/data \
-e POSTGRES_PASSWORD=mysecretpassword \
-v /DATA/database/psql9.6:/var/lib/postgresql/data \
-p 5432:5432 \
--entrypoint "docker-entrypoint.sh" \
postgres:9.6.17 \
postgres

使用docker,新建用户,新建数据库

1
2
3
4
5
6
7
docker exec -it some-postgres sh
su - postgres

createuser liantian
createdb -O liantian liantian
psql
alter user liantian with encrypted password '123456';

学习到了奇怪的知识点..

  • 日志重定向到 /dev/stdout
  • 错误日志重定向到/dev/stderr

然后通过docker logs 查看日志。。

下面是正文…

新建一个Dockerfile

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
FROM alpine:latest

RUN sed -i 's/dl-cdn.alpinelinux.org/mirrors.tuna.tsinghua.edu.cn/g' /etc/apk/repositories \
&& apk update \
&& apk upgrade \
&& apk add --no-cache nginx \
&& rm -rf /var/cache/apk/* \
&& ln -sf /dev/stdout /var/log/nginx/access.log \
&& ln -sf /dev/stderr /var/log/nginx/error.log \
&& rm -f /etc/nginx/conf.d/default.conf \
&& mkdir -p /config/ \
&& mkdir -p /wwwroot/ \
&& mkdir -p /run/nginx \
&& sed -i 's|include /etc/nginx/conf.d/\*.conf;| include /config/nginx/conf.d/\*.conf;|g' /etc/nginx/nginx.conf


STOPSIGNAL SIGTERM

EXPOSE 80

VOLUME ["/config", "/wwwroot"]
CMD ["nginx", "-g", "daemon off;"]

这里遇到一个坑

  1. 启动报错nginx: [emerg] open() "/run/nginx/nginx.pid" failed (2: No such file or directory),解决方法:加入mkdir -p /run/nginx \

编译Dockerfile

1
docker build --tag nginx:20200509 .

创建配置文件目录

1
2
sudo mkdir -p /DATA/config/nginx/conf.d/
sudo mkdir -p /DATA/wwwroot/

新建配置文件/DATA/config/nginx/conf.d/default.conf

1
2
3
4
5
6
7
8
9
10
11
12
13
14
server {
listen 80 default_server;
listen [::]:80 default_server;
root /wwwroot;
server_name _;
autoindex on;
autoindex_localtime on;

location / {
# First attempt to serve request as file, then
# as directory, then fall back to displaying a 404.
try_files $uri $uri/ =404;
}
}

创建容器

1
2
3
4
5
6
7
8
9
10
11
docker create \
-p 80:80 \
-v /DATA/config:/config \
-v /DATA/wwwroot:/wwwroot \
--log-driver local \
--log-opt max-size=10m \
--log-opt max-file=3 \
--log-opt compress=true \
--restart always \
--name nginx \
nginx:20200509

导出容器

1
docker export nginx  > nginx.export.tar

清理

1
2
3
docker stop nginx
docker container rm nginx
docker image rm nginx:20200509

在生产机导入

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
docker import nginx.export.1.16.1.tar  nginx:1.16.1

docker create \
-p 80:80 \
-v /DATA/config:/config \
-v /DATA/wwwroot:/wwwroot \
--log-driver local \
--log-opt max-size=10m \
--log-opt max-file=3 \
--log-opt compress=true \
--restart always \
--name nginx \
nginx:1.16.1 \
nginx -g 'daemon off;'