深度优先

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

0%

Docker 安装

卸载旧版本

1
2
3
4
5
6
7
8
sudo yum remove docker \
docker-client \
docker-client-latest \
docker-common \
docker-latest \
docker-latest-logrotate \
docker-logrotate \
docker-engine

安装依赖包

1
sudo yum install -y yum-utils device-mapper-persistent-data lvm2

添加 yum 软件源

鉴于国内网络问题,建议使用国内源,官方源请在注释中查看,执行下面的命令添加yum软件源:

1
2
3
4
5
6
7
8
9
sudo yum-config-manager \
--add-repo \
https://mirrors.ustc.edu.cn/docker-ce/linux/centos/docker-ce.repo


# 官方源
# $ sudo yum-config-manager \
# --add-repo \
# https://download.docker.com/linux/centos/docker-ce.repo

安装 Docker CE

更新yum软件源缓存,并安装docker-ce

1
2
sudo yum makecache fast
sudo yum install docker-ce

启动 Docker CE

1
2
sudo systemctl enable docker
sudo systemctl start docker

测试 Docker 是否安装正确

1
docker run hello-world

若能正常输出以下信息,则说明安装成功。

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
Unable to find image 'hello-world:latest' locally
latest: Pulling from library/hello-world
d1725b59e92d: Pull complete
Digest: sha256:0add3ace90ecb4adbf7777e9aacf18357296e799f81cabc9fde470971e499788
Status: Downloaded newer image for hello-world:latest

Hello from Docker!
This message shows that your installation appears to be working correctly.

To generate this message, Docker took the following steps:
1. The Docker client contacted the Docker daemon.
2. The Docker daemon pulled the "hello-world" image from the Docker Hub.
(amd64)
3. The Docker daemon created a new container from that image which runs the
executable that produces the output you are currently reading.
4. The Docker daemon streamed that output to the Docker client, which sent it
to your terminal.

To try something more ambitious, you can run an Ubuntu container with:
$ docker run -it ubuntu bash

Share images, automate workflows, and more with a free Docker ID:
https://hub.docker.com/

For more examples and ideas, visit:
https://docs.docker.com/get-started/

镜像加速

国内从Docker Hub拉取镜像有时会遇到困难,此时可以配置镜像加速器:

1
vi /etc/docker/daemon.json

添加镜像地址:

1
2
3
4
5
6
{
"registry-mirrors": [
"https://dockerhub.azk8s.cn",
"https://hub-mirror.c.163.com"
]
}

之后重新启动服务:

1
2
sudo systemctl daemon-reload
sudo systemctl restart docker

或者

1
2
3
4
5
6
7
8
9
10
11
sudo tee /etc/docker/daemon.json <<-'EOF'
{
"registry-mirrors": [
"https://od404bh3.mirror.aliyuncs.com",
"https://dockerhub.azk8s.cn",
"https://hub-mirror.c.163.com"
]
}
EOF
sudo systemctl daemon-reload
sudo systemctl restart docker

执行 docker info,如果从结果中看到了如下内容,说明配置成功:

1
2
Registry Mirrors:
https://dockerhub.azk8s.cn/

测试镜像地址

国内无法直接获取 gcr.io/* 镜像:

1
docker pull gcr.azk8s.cn/google_containers/hyperkube-amd64:v1.9.2

若能拉取到则配置成功!

Nginx 代理服务器配置

创建挂载文件夹

1
2
3
4
cd /opt/
mkdir -p docker/nginx
cd docker/nginx/
mkdir {logs,conf,cert,website}

logs存放nginx相关日志,conf存放default.conf文件,
cert存放https证书,website存放静态网页文件

创建default.conf文件

1
vi conf/default.conf

编辑内容:

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
server {
listen 80;
server_name localhost;

#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;

add_header Access-Control-Allow-Origin *;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html =404; #解决angular刷新404问题

if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
return 204;
}
}

location ^~ /api/signalR
{
proxy_http_version 1.1;
proxy_set_header Upgrade $http_upgrade;
proxy_set_header Cpnnection "upgrade";
proxy_pass http://192.168.0.101:443;
proxy_cache_bypass $http_upgrade;
}

location ^~ /api/ {
if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
return 204;
}
proxy_pass http://192.168.0.101:443;
}
#error_page 404 /404.html;

# redirect server error pages to the static page /50x.html
# error_page 500 502 503 504 /50x.html;
# location = /50x.html {
# root /usr/share/nginx/html;
# }

# proxy the PHP scripts to Apache listening on 127.0.0.1:80
#
# location ~ \.php$ {
# proxy_pass http://127.0.0.1;
# }

# pass the PHP scripts to FastCGI server listening on 127.0.0.1:9000
#
# location ~ \.php$ {
# root html;
# fastcgi_pass 127.0.0.1:9000;
# fastcgi_index index.php;
# fastcgi_param SCRIPT_FILENAME /scripts$fastcgi_script_name;
# include fastcgi_params;
# }

# deny access to .htaccess files, if Apache's document root
# concurs with nginx's one
#
#location ~ /\.ht {
# deny all;
#}
}

创建nginx.conf文件

1
vi nginx.conf

编辑内容:

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
user  nginx;
worker_processes 1;

error_log /var/log/nginx/error.log warn;
pid /var/run/nginx.pid;

events {
worker_connections 1024;
}

http {
include /etc/nginx/mime.types;
default_type application/octet-stream;

log_format main '$remote_addr - $remote_user [$time_local] "$request" '
'$status $body_bytes_sent "$http_referer" '
'"$http_user_agent" "$http_x_forwarded_for"';

access_log /var/log/nginx/access.log main;

server {
listen 80;
server_name localhost;

#charset koi8-r;
#access_log /var/log/nginx/host.access.log main;

add_header Access-Control-Allow-Origin *;
location / {
root /usr/share/nginx/html;
index index.html index.htm;
try_files $uri $uri/ /index.html =404;

if ($request_method = 'OPTIONS') {
add_header Access-Control-Allow-Origin *;
add_header Access-Control-Allow-Methods GET,POST,PUT,DELETE,OPTIONS;
return 204;
}
}
}

# 配置http重定向到https
server {
listen 80;
server_name blog.bfsdfs.com;
rewrite ^(.*)$ https://$host$1 permanent;
}
# 配置https证书,和端口转发
server {
listen 443 ssl;
server_name www.test.com;
ssl_certificate cert/www.test.pem;
ssl_certificate_key cert/www.test.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_prefer_server_ciphers on;
location / {
proxy_pass http://192.168.0.4:81/;
proxy_set_header Host $host;
proxy_set_header X-Real-Ip $remote_addr;
proxy_set_header X-Forwarded-For $remote_addr;
}
}

sendfile on;
#tcp_nopush on;

keepalive_timeout 65;

#gzip on;

include /etc/nginx/conf.d/*.conf;
}

cert目录存放https证书

运行Nginx

1
2
3
4
5
6
7
docker run --name nginx -d -p 80:80 -p 443:443 --restart always \
-v /opt/docker/nginx/website:/usr/share/nginx/html \
-v /opt/docker/nginx/logs:/var/log/nginx \
-v /opt/docker/nginx/conf:/etc/nginx/conf.d \
-v /opt/docker/nginx/nginx.conf:/etc/nginx/nginx.conf \
-v /opt/docker/nginx/cert:/etc/nginx/cert \
nginx

查看运行情况

1
docker ps

MySql 安装与配置

创建MySql挂载目录

1
2
3
4
cd /opt/
mkdir -p docker/mysql
cd docker/mysql/
mkdir {conf,data,logs}

说明:conf存放配置文件,data存放数据文件,logs存放日志文件

安装MySql

1
2
3
4
5
docker run -p 8606:3306 --name mysql --restart always \
-v /opt/docker/mysql/conf:/etc/mysql/conf.d \
-v /opt/docker/mysql/logs:/var/log/mysql \
-v /opt/docker/mysql/data:/var/lib/mysql \
-e MYSQL_ROOT_PASSWORD=123456 -d mysql:8.0

查看MySql运行情况

1
docker ps

允许远程连接

进入到mysql容器

1
docker exec -it mysql /bin/bash

登录mysql数据库

1
mysql -u root -p 123456

支持远程连接

1
2
ALTER user 'root'@'%' IDENTIFIED WITH mysql_native_password BY '123456';
FLUSH PRIVILEGES;

开放3306端口

1
2
3
firewall-cmd --zone=public --add-port=3306/tcp –permanent 
firewall-cmd --reload
firewall-cmd --zone=public --query-port=3306/tcp

数据库连接字符串

1
server=ipaddr,port=3306,username=root,password=123456

安装 .Net Core SDK

通过yum安装

1
2
3
sudo rpm -Uvh https://packages.microsoft.com/config/rhel/7/packages-microsoft-prod.rpm
sudo yum update
sudo yum install dotnet-sdk-2.1

通过下载文件安装

以.net core 3.1为例:

1
2
3
4
5
6
cd /opt/docker
mkdir {download,dotnet}
cd download
wget https://download.visualstudio.microsoft.com/download/pr/c4b503d6-2f41-4908-b634-270a0a1dcfca/c5a20e42868a48a2cd1ae27cf038044c/dotnet-sdk-3.1.101-linux-x64.tar.gz

tar zxf dotnet-sdk-3.1.101-linux-x64.tar.gz -C /opt/docker/dotnet

配置环境变量:

1
2
3
4
5
6
7
8
9
10
11
vi /etc/profile

# 在文件末尾添加
export DOTNET_ROOT=/opt/docker/dotnet
export PATH=$PATH:/opt/docker/dotnet

# 重新加载环境变量
source /etc/profile

# 查看net core信息
dotnet

部署 .Net Core WebApi

创建挂载目录

1
2
cd /opt/docker
mkdir webapi

创建Dockerfile文件

1
vi /opt/docker/webapi/Dockerfile

编辑内容:

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
FROM mcr.microsoft.com/dotnet/core/aspnet:2.2-stretch-slim AS base
WORKDIR /app
EXPOSE 80

ENV LANG C.UTF-8

# 设置代理
# ENV http_proxy http://your-proxy-server:port
# ENV https_proxy https://your-proxy-server:port
# ENV no_proxy 127.0.0.1,/var/run/docker.sock

# 开发环境
ENV ASPNETCORE_ENVIRONMENT "Development"
ENV ASPNETCORE_WEBWEBSITE "测试环境API"

# 正式环境
# ENV ASPNETCORE_ENVIRONMENT "Production"
# ENV ASPNETCORE_WEBWEBSITE "正式环境API"

# 替换源
RUN sed -i 's#http://deb.debian.org#https://mirrors.163.com#g' /etc/apt/sources.list
RUN apt-get clean

# 安装 libgdiplus
RUN apt-get update
RUN apt-get install -y libgdiplus
RUN cd /usr/lib
RUN ln -s libgdiplus.so gdiplus.dll

# 时区设置
RUN ln -sf /usr/share/zoneinfo/Asia/Shanghai /etc/localtime
RUN echo 'Asia/Shanghai' >/etc/timezone

ENTRYPOINT ["dotnet", "api.dll"]

build生成docker镜像

1
docker build -t api-images .

运行docker镜像

1
docker run -d --name api --restart always -v /opt/docker/api:/app -p 82:80 api-images

安装 SqlServer

下载SQL Server镜像

1
sudo docker pull microsoft/mssql-server-linux:2017-latest

启动SQL Server容器:

1
2
3
4
5
sudo docker run -e 'ACCEPT_EULA=Y' -e 'MSSQL_SA_PASSWORD=123456' \
-p 1433:1433 --name sql-server --restart always \
-d microsoft/mssql-server-linux:2017-latest

# 具体参数意义,请参看微软文档。(MSSQL_SA_PASSWORD为初始密码,可以自己修改)

修改 sa 密码

1
2
3
sudo docker exec -it sql-server /opt/mssql-tools/bin/sqlcmd \
-S localhost -U SA -P 'Asdfgh123' \
-Q 'ALTER LOGIN SA WITH PASSWORD="qazWSX123"'

连接数据库

1
sudo docker exec -it sql-server "bash"

简单操作

1
2
3
CREATE DATABASE TestDB
SELECT Name from sys.Databases
GO

外部访问SQL Server

安装 sqlcmd,前提已经安装了 nodejs(node 安装请自行搜索)
执行命令:

1
npm install -g sqlcmdjs

执行查询命令:

1
sqlcmd -s 127.0.0.1 -o 1433 -u SA -p "123456" "select name, database_id from sys.databases"

安装 Redis

创建挂载目录

创建宿主机 redis 容器的数据和配置文件目录

1
2
3
mkdir /opt/docker/redis/{conf,data} -p
cd /opt/docker/redis

获取Redis的默认配置模版

1
2
3
4
5
6
7
8
9
10
11
# 获取 redis 的默认配置模版
# 这里主要是想设置下 redis 的 log / password / appendonly
# redis 的 docker 运行参数提供了 --appendonly yes 但没 password
wget https://raw.githubusercontent.com/antirez/redis/4.0/redis.conf -O conf/redis.conf

# 直接替换编辑
sed -i 's/logfile ""/logfile "access.log"/' conf/redis.conf
sed -i 's/# requirepass foobared/requirepass 123456/' conf/redis.conf
sed -i 's/appendonly no/appendonly yes/' conf/redis.conf

# 这里可能还需配置一些 bind protected-mode

创建Redis容器

创建并运行一个名为 redis 的容器

1
2
3
4
5
6
7
8
docker run \
-p 6379:6379 \
-v $PWD/data:/data \
-v $PWD/conf/redis.conf:/etc/redis/redis.conf \
--privileged=true \
--name redis --restart always \
-d redis redis-server /etc/redis/redis.conf

进入到容器里面

1
docker exec -it redis bash redis-cli

配置外网可以访问

更改redis.conf文件

1
2
3
4
5
# 将这个注释掉
# bind 127.0.0.1

# no 改为 yes
protected-mode yes

集成 Gitlab Runner

创建挂载目录

1
2
mkdir -p /opt/docker/gitlab/config
mkdir -p /opt/docker/output

说明:output为gitlab-runner打包后输出文件的目录,可以让其他容器挂载这个目录

授权添加用户

1
2
3
4
chmod 777 /var/run/docker.sock

useradd gitlab-runner
usermod -aG docker gitlab-runner

创建gitlab-runner容器

1
2
3
4
5
6
7
8
docker run -d --name gitlab-runner --restart always \
-v /opt/docker/gitlab/config:/etc/gitlab-runner \
-v /var/run/docker.sock:/var/run/docker.sock \
-v /usr/bin/docker:/usr/bin/docker \
-v /opt/docker/blog:/tmp/output/blog \
-v /opt/docker/webapi:/tmp/output/webapi \
-v /opt/docker/iot-client:/tmp/output/iot-client \
gitlab/gitlab-runner:latest

注册gitlab-runner

1
docker exec -it gitlab-runner gitlab-ci-multi-runner register

依次输入 gitlab地址,token,tag,描述,shell

进入容器

1
2
3
4
5
6
7
8
docker exec -it gitlab-runner /bin/bash

apt-get update
apt-get install rpm
apt-get install yum
apt-get install -y libltdl7

# 安装.net core sdk 可参考上面的

编辑 .gitlab-ci.yml

先以一个最简单的为例,如下:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
stages:
- deploy
deploy_job:
stage: deploy
script:
- cd /tmp/blog
- git pull
- cd demo
- source /etc/profile
- version=`date "+%Y.%m.%d.1"`
- dotnet build -c Release -p:Version=${version} -o /tmp/output
- docker stop blog-api
- rm -rf /tmp/blog-website/*
- cp -r /tmp/output /tmp/blog-website
- docker start blog-api

程序里获取版本信息:

1
2
3
4
5
6
7
8
9
10
11
var version = FileVersionInfo.GetVersionInfo(Assembly.GetEntryAssembly().Location).FileVersion;
if (env.IsDevelopment())
{
app.UseDeveloperExceptionPage();
app.UseSwagger();
app.UseSwaggerUI(option =>
{
option.SwaggerEndpoint("/swagger/v2/swagger.json", $"业务-接口文档 v{version}");
option.SwaggerEndpoint("/swagger/v1/swagger.json", $"基础-接口文档 v{version}");
});
}

安装Nodejs

1
2
3
4
5
6
7
8
9
10
wget http://nodejs.org/dist/v0.10.30/node-v0.10.30-linux-x64.tar.gz

tar --strip-components 1 -xzvf node-v* -C /usr/local

node --version

npm install -g n

n latest

docker 常用命令

查看容器信息

1
docker inspect

查看日志信息

1
docker logs -t --tail=10 <容器id>

容器自动启动设置

在运行时容器时设置,在run命令后加 –restart=always ,如:

1
docker run --restart=always -d --name api -v /opt/docker/api:/app -p 82:80 api-images

或,给已运行的容器添加自启动,命令:

1
docker update --restart=always <容器Id>

Vim 安装

1
2
apt-get update
apt-get install vim

开放端口

1
2
3
firewall-cmd --zone=public --add-port=80/tcp –permanent 
firewall-cmd –reload #重新载入
firewall-cmd --zone=public --query-port=80/tcp #查看

容器间通信

bridge方式:

1
2
3
4
docker network create --driver bridge mysql-network
docker network ls
docker inspect mysql-network
docker run -d --network mysql-network --name 容器名 镜像名

coverage report

读取文件夹下面的*.md文件,正则匹配

1
![](http****)

解析到图片的网络地址,然后模拟请求将图片下载到本地替换md中的url重新写到md文件中,代码如下:

解析Md文件

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
private static void Main(string[] args)
{
var path = @"C:\xguan\demo\hexo\source\_posts\";

var dir = new DirectoryInfo(path);
var files = dir.GetFiles($"*.md");
foreach (var file in files)
{
StreamReader sr = new StreamReader(file.FullName, Encoding.UTF8);
var source = sr.ReadToEnd();

Regex regex = new Regex(@"!\[\]\((.*?)\)", RegexOptions.IgnoreCase);
MatchCollection matches = regex.Matches(source);
foreach (var matche in matches)
{
var url = matche.ToString().Replace("![](", "").TrimEnd(')');

var fileName = Path.GetFileName(url);
if (url.IndexOf("?") != -1)
{
fileName = Path.GetFileName(url.Substring(0, url.IndexOf("?")));
}

if (Path.GetExtension(fileName).Length == 0)
{
fileName = $"{fileName}.png";
}
var newImgSrc = $"./images/{fileName}";

if (DownloadFile(url, newImgSrc))
{
source = source.Replace(url, newImgSrc);
Console.WriteLine($"处理前:{url}");
Console.WriteLine($"处理后:{newImgSrc}");
Console.WriteLine();
}
}
//copy 重新写文件
using (FileStream fs = new FileStream($"./copy/{file.Name}", FileMode.OpenOrCreate))
{
byte[] data = System.Text.Encoding.UTF8.GetBytes(source);
fs.Write(data, 0, data.Length);
}
System.Threading.Thread.Sleep(1000);
}
Console.WriteLine("处理完成");
Console.ReadLine();
}

下载图片

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
private static bool DownloadFile(string url, string filename)
{
if (!url.StartsWith("http"))
{
return false;
}
try
{
HttpWebRequest Myrq = (HttpWebRequest)HttpWebRequest.Create(url);
using (HttpWebResponse myrp = (HttpWebResponse)Myrq.GetResponse())
{
using (Stream st = myrp.GetResponseStream())
{
Stream so = new FileStream($"{filename}", FileMode.OpenOrCreate);
byte[] by = new byte[1024];
int osize = st.Read(by, 0, (int)by.Length);
while (osize > 0)
{
so.Write(by, 0, osize);
osize = st.Read(by, 0, (int)by.Length);
}
so.Close();
Myrq.Abort();
}
};
return true;
}
catch (System.Exception e)
{
Console.WriteLine(e.Message);
return false;
}
}

Http 请求响应状态码

前后端分离开发项目中需要定义接口返回状态码,可能每个公司都有自己的一套规范,传统做法是任何请求都返回状态码200,然后在body里包一层 {code:0,message:'xxxx',result:object},由于现在前端三大框架越来越成熟,更提倡我们采用http响应的状态码来区分请求的接口,每次只需根据状态码来判断,通常情况下不需要特地解析body里面的数据

四个动词

  • GET:读取(Read)—>查询操作
  • POST:新建(Create)—>添加操作
  • PUT:更新(Update)—>修改操作
  • DELETE:删除(Delete)—>删除操作

结合拦截器,可根据http状态码 做全局的操作提示

状态码

首先状态码可以大致分为五类:

  • 1XX:保持连接
  • 2XX:操作成功
  • 3XX:重定向
  • 4XX:客户端错误
  • 5XX:服务端错误

2XX

  • GET: 200 OK
  • POST: 201 Created
  • PUT: 202 Updated
  • DELETE: 204 No Content

4XX

  • 401 Unauthorized:用户未提供身份验证凭据,或者没有通过身份验证。
    如:验证码错误/账号密码错误/没有携带Token

  • 403 Forbidden:用户通过了身份验证,但是不具有访问资源所需的权限。
    如:没有权限访问此接口

  • 404 Not Found:所请求的资源不存在,或不可用。
    如:删除/更新时资源不存在

  • 409 Conflict:通用冲突,添加/更新时数据已存在。
    如:添加/更新时 如机器码已存在,用户邮箱已存在

  • 422 Unprocessable Entity :验证不通过。
    如:添加/更新时字段校验不过

5XX

  • 500 Internal Server Error:客户端请求有效,服务器处理时发生了意外。
  • 503 Service Unavailable:服务器无法处理请求,一般用于网站维护状态。