Microk8s helm rancher
helm repo add rancher-latest https://releases.rancher.com/server-charts/latest
kubectl create namespace cattle-system
kubectl apply --validate=false -f https://github.com/jetstack/cert-manager/releases/download/v1.0.4/cert-manager.crds.yaml
kubectl create namespace cert-manager
helm repo add jetstack https://charts.jetstack.io
helm repo update
helm install \
cert-manager jetstack/cert-manager \
--namespace cert-manager \
--version v1.0.4
helm install rancher rancher-latest/rancher \
--namespace cattle-system \
--set hostname=rancher.lizhe.com
Sonarqube pipeline
pipeline {
agent none
environment {
SONAR_LOGIN='xxxxxxxxxxxxxxxxxxxxxx'
}
stages {
stage ('Checkout') {
agent any
steps {
sh "rm -rf /var/jenkins_home/workspace/${env.JOB_NAME}/*"
git branch: 'develop', credentialsId: 'xxxxxxxxxxxxxxxxxxxx', url: 'https://github.com/xxxxxx/xxxxxxxx.git'
}
}
stage('Scan') {
agent {
docker {
image 'docker:20.10.6'
args "-v /var/run/docker.sock:/var/run/docker.sock -v /var/jenkins_home/workspace/${env.JOB_NAME}/:/usr/src"
}
}
steps {
dir("/var/jenkins_home/workspace/${env.JOB_NAME}/") {
sh """
ls /usr/src
rm -rf /usr/src/*
ls /usr/src
ls /var/jenkins_home/workspace/${env.JOB_NAME}
cp -r /var/jenkins_home/workspace/${env.JOB_NAME}/* /usr/src/
ls /usr/src
docker run --rm -e SONAR_HOST_URL="http://10.10.10.198:9000/" -e SONAR_LOGIN='${env.SONAR_LOGIN}' -v /var/jenkins_home/workspace/${env.JOB_NAME}/:/usr/src sonarsource/sonar-scanner-cli \
-Dsonar.projectKey=app-sonarqube -Dsonar.sources=./
"""
}
}
}
}
}
Microk8s prometheus operator
microk8s enable prometheus
如果安装过程中报错,尝试删除相关文件夹
sudo rm -rf /var/snap/microk8s/2262/kube-prometheus
使用 kubectl port-forward 登入
microk8s kubectl port-forward grafana-6b8df57c5b-z626n 3000:3000 -n monitoring
输入系统刚才提示的密码
admin/admin
创建一个 nodeport
kind: Service
apiVersion: v1
metadata:
name: grafananp
namespace: monitoring
labels:
app: grafana
spec:
ports:
- name: http
protocol: TCP
port: 3000
targetPort: 3000
nodePort: 30036
selector:
app: grafana
type: NodePort
sessionAffinity: None
externalTrafficPolicy: Cluster
Microk8s
- Install MicroK8s on Linux
sudo snap install microk8s --classic
2. Add your user to the microk8s admin group
sudo usermod -a -G microk8s $USER
sudo chown -f -R $USER ~/.kube
su - $USER
3. Check the status while Kubernetes starts
microk8s status --wait-ready
4. Turn on the services you want
microk8s enable dashboard dns ingress
5. Start using Kubernetes
microk8s kubectl get all --all-namespaces
alias mkctl="microk8s kubectl"
mkctl get all -A
6. Access the Kubernetes dashboard
microk8s dashboard-proxy
7. Start and stop Kubernetes to save battery
microk8s start
microk8s stop
8. Multi-node, highly available Kubernetes with MicroK8s
在 ubuntu1 上
microk8s add-node
在 ubuntu2 上
microk8s join 192.168.194.196:25000/24bfc55c51720b62f15caefb95a8f211/c11a38c71679
此时可以看到 ubuntu1 作为 master 节点,ubuntu2 作为 slave 节点
Elasticsearch 基本操作
ELasticsearch 会读取映射字段的所有默认属性,并开始按照如下方式处理它们
如果该字段已经存在于映射中并且该字段值有效(它与正确对的类型匹配),那么ELasticsearch 不需要更改当前映射
如果该字段已经存在于映射中,但该字段的值具有不同的类型,则ELasticsearch将尝试升级该字段的类型(例如,从integer整型更改为long长整型)。如果类型不兼容则抛出异常,索引过程将失败
如果该字段不存在,那么ELasticsearch将尝试自动检测字段的类型。它将使用新的字段映射更新映射
为了或得更好的索引结果和性能,需要手动定义映射
减少磁盘上的索引大小(禁用自定义字段的功能)
仅对感兴趣的字段建立索引(这样做通常会加快速度)
预处理数据以进行快速搜索和实时分析
正确定义必须一多个标记分析字段还是将其视为单个标记
创建索引
PUT /myindex
{
"settings": {
"index": {
"number_of_shards": 2,
"number_of_replicas": 1
}
}
}
DELETE myindex
携带mapping信息
PUT /myindex
{
"settings": {
"index": {
"number_of_shards": 2,
"number_of_replicas": 1
}
},
"mappings": {
"properties": {
"id":{
"type": "keyword",
"store": true
},
"name":{
"type": "text",
"store": true
},
"age":{
"type": "integer",
"store": true
},
"desc":{
"type": "text",
"store": true
}
}
}
}
打开或关闭索引
如果想要保留数据并且节省资源 (内存或CPU)
则删除索引的一种不错的代替方法是,关闭它们
ELasticsearch 允许你打开或者关闭索引,使其进入在线或离线模式
POST /myindex/_close
POST /myindex/_open
索引关闭后,集群上没有开销(元数据状态除外):
索引分片已关闭,并且它们不适用文件描述符、内存和线程
关闭索引有很多用例
它可以禁用基于日期的索引(按日期存储其记录的索引)
当你在集群中的所有活动索引上进行搜索并且不想在某些索引中进行搜索时(在这种情况下,使用别名是最佳解决方案,但是也可以使用具有已关闭索引的别名来实现相同的概念)
创建映射
PUT /myindex/_mapping
{
"properties": {
"id":{
"type": "keyword",
"store": true
},
"name":{
"type": "text",
"store": true
},
"age":{
"type": "integer",
"store": true
},
"desc":{
"type": "text",
"store": true
}
}
}
GET /myindex/_mapping?pretty
没有删除索引的操作,要删除或者更改映射,需要执行以下步骤
- 使用新的或修改的映射创建新索引
- 重新索引所有记录
- 删除具有错误映射的旧索引
重建索引
有很多常见的情况都涉及更改映射。
由于无法删除已经定义的映射,因此需要为索引数据重建索引
- 更改映射分析器
- 在映射中添加新字段,随后你需要重新处理所有记录以搜索新的子字段
- 删除未使用的映射
- 更改需要新映射的记录结构
POST /_reindex?pretty=true
{
"source": {
"index": "myindex"
},
"dest": {
"index": "myindex2"
}
}
- 创建 myindex ,设置 mapping ,传入数据
- 创建 myindex2 , 设置 mapping
- 调用上面脚本
- myindex2 或得数据
刷新索引
ELasticsearch 允许用户对索引的强制刷新来控制搜索器的状态。
如果未强制刷新,则新索引的文档只能在固定的时间间隔 默认1秒 后才能被搜索
POST /myindex/_refresh
调用刷新的最佳时间是对大量数据建立索引之后,以确保可以立即搜搜记录,通过将 refresh = true 添加为查询参数,可以在文档创建索引期间强制刷新
POST /myindex/_doc/xiaoming?refresh=true
{
"name": "xiaoming"
}
冲洗索引
出于性能原因,ELasticsearch 将一些数据存储在内存中和事务日志上,如果需要释放内存,则需要清空事务日志,并确保将数据安全地写入磁盘,因此需要冲洗 Flush 索引
POST /myindex/_flush
POST /_flush
强制合并索引
ELasticsearch 核心是基于 Lucene 的, 而Lucene则会将数据分段存储在磁盘上。
在索引有效期内,会创建和更改许多段。随着段号的增加,由于读取所有段所需的时间,搜索速度会降低。强制合并 ForceMerge 操作使我们可以合并索引,以提高搜索性能并减少分段。
POST /myindex/_forcemerge
POST /_forcemerge
Lucene 中的forcemerge 操作试图通过 删除未使用的段,清除已删除的文档并以最小的段数重建索引,从而以大量 I/O 的方式减少分段
主要优点在于
- 减少了文件描述符
- 释放段读取器使用的内存
- 减少了段的管理,提高了搜索过程中的性能
过程中索引可能会没有响应
使用索引别名
需要跨越索引进行查询时,可以使用别名,将它们分组
GET /_aliases
GET /_alias
PUT /myindex/_alias/myali
PUT /.kibana_1/_alias/myali
GET /myali
DELETE /myindex/_alias/myali
DELETE /*/_alias/myali
滚动索引
滚动索引是一个特殊的别名,当条件之一被匹配时,该别名将管理新索引的自动创建
命名约定由 ELasticsearch 管理, ELasticsearch 会自动增加索引名称的数字部分,默认情况下,它使用 6个结尾数字
PUT /mylogs-000001
{
"aliases": {
"k8slogs": {}
}
}
POST /k8slogs/_rollover
{
"conditions": {
"max_age": "7d"
, "max_docs": 1000
},
"settings": {
"index.number_of_shards": 3
}
}
缩小索引
ELasticsearch 提供了优化索引的新方法
使用 缩小 Shrink API,可以减少索引的分片数量。
此功能针对以下几种常见方案
- 在初始设计大小期间,分片数量往往会指定的不合适。
- 减少分片数量以减少内存和资源使用
- 减少分片数量以加快搜索速度
缩小API 是通过执行以下步骤来减少分片的数量
ELasticsearch 创建一个新的索引目标,其定义与原索引相同,但主分片数量更少
ELasticsearch 将分片段从原索引硬链接(或复制)到目标索引
要缩小索引,可执行以下步骤
- 所有被缩小的索引的主分片都需要在同一节点上。
所以这里需要节点名称
GET /_nodes?pretty
“name” : “51929c9c249f”,
{
"_nodes" : {
"total" : 1,
"successful" : 1,
"failed" : 0
},
"cluster_name" : "docker-cluster",
"nodes" : {
"qFMuUvbPTJeB8uism4TD6Q" : {
"name" : "51929c9c249f",
"transport_address" : "172.17.0.2:9300",
"host" : "172.17.0.2",
"ip" : "172.17.0.2",
"version" : "7.7.1",
"build_flavor" : "default",
"build_type" : "docker",
"build_hash" : "ad56dce891c901a492bb1ee393f12dfff473a423",
"total_indexing_buffer" : 107374182,
"roles" : [
2. 强制固定索引到这个要使用的节点
PUT /myindex/_settings
{
"settings": {
"index.routing.allocation.require._name": "51929c9c249f",
"index.blocks.write": true
}
}
3. 检查是否所有分片都已重定位,通过检查green状态 ( yellow 就说明可以了)
GET /_cluster/health?pretty
4. 索引应处于只读状态才能进行缩小操作
PUT /myindex/_settings
{
"index.blocks.write": true
}
5. 创建缩小的索引
POST /myindex/_shrink/reduced_index
{
"settings": {
"index.number_of_replicas": 1,
"index.number_of_shards": 1,
"index.codec": "best_compression"
}
}
6. 检查状态,等待 yellow
GET /_cluster/health?wait_for_status=yellow
7. 删除刚刚的只读配置
PUT /myindex/_settings
{
"index.blocks.write": false
}
AES加密golang实现
本例将使用一个 golang 的rest接口 接收一个文件上传,然后保存文件到磁盘
先来看golang的rest,不加密的情况
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
http.HandleFunc("/hello", doLogic)
http.ListenAndServe(":3070", nil)
}
func doLogic(w http.ResponseWriter, r *http.Request) {
r.ParseMultipartForm(32 << 20) // 32MB
file, handler, err := r.FormFile("myfile")
if err != nil {
fmt.Println(err)
return
}
defer file.Close()
fmt.Fprintf(w, "%v", handler.Header)
f, err := os.OpenFile("/tmp2/"+handler.Filename, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Println(err)
return
}
defer f.Close()
io.Copy(f, file)
}
然后使用postman来上传一个helloworld文件
{
"info": {
"_postman_id": "a4becb7c-5daf-491a-991d-1e93d451a28d",
"name": "New Collection",
"schema": "https://schema.getpostman.com/json/collection/v2.1.0/collection.json"
},
"item": [
{
"name": "New Request",
"request": {
"method": "POST",
"header": [
{
"key": "Content-Type",
"value": "multipart/form-data",
"type": "text"
}
],
"body": {
"mode": "formdata",
"formdata": [
{
"key": "myfile",
"type": "file",
"src": "/Users/lizhe/helloworld.txt"
}
]
},
"url": {
"raw": "192.168.194.191:3070/hello",
"host": [
"192",
"168",
"194",
"191"
],
"port": "3070",
"path": [
"hello"
]
}
},
"response": []
}
]
}
检查一下上传进来的内容
下面我们来创建 encrypt.go , 用来加密
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
"io"
"io/ioutil"
)
func main() {
fmt.Println("Encryption Program v0.01")
text := []byte("this is a message need to be encrypted")
key := []byte("thisismypassword")
// generate a new aes cipher using our 32 byte long key
c, err := aes.NewCipher(key)
// if there are any errors, handle them
if err != nil {
fmt.Println(err)
}
// gcm or Galois/Counter Mode, is a mode of operation
// for symmetric key cryptographic block ciphers
// - https://en.wikipedia.org/wiki/Galois/Counter_Mode
gcm, err := cipher.NewGCM(c)
// if any error generating new GCM
// handle them
if err != nil {
fmt.Println(err)
}
// creates a new byte array the size of the nonce
// which must be passed to Seal
nonce := make([]byte, gcm.NonceSize())
// populates our nonce with a cryptographically secure
// random sequence
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
fmt.Println(err)
}
// here we encrypt our text using the Seal function
// Seal encrypts and authenticates plaintext, authenticates the
// additional data and appends the result to dst, returning the updated
// slice. The nonce must be NonceSize() bytes long and unique for all
// time, for a given key.
fmt.Println(gcm.Seal(nonce, nonce, text, nil))
// the WriteFile method returns an error if unsuccessful
err = ioutil.WriteFile("myfile.data", gcm.Seal(nonce, nonce, text, nil), 0777)
// handle this error
if err != nil {
// print it out
fmt.Println(err)
}
}
decrypt.go 用来解密
package main
import (
"crypto/aes"
"crypto/cipher"
"fmt"
"io/ioutil"
)
func main() {
fmt.Println("Decryption Program v0.01")
key := []byte("thisismypassword")
ciphertext, err := ioutil.ReadFile("myfile.data")
// if our program was unable to read the file
// print out the reason why it can't
if err != nil {
fmt.Println(err)
}
c, err := aes.NewCipher(key)
if err != nil {
fmt.Println(err)
}
gcm, err := cipher.NewGCM(c)
if err != nil {
fmt.Println(err)
}
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
fmt.Println(err)
}
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(plaintext))
}
密码错误的情况
密码正确的情况
最后把 main.go 、encrypt.go 和 encrypt.go 整合一下
main.go
package main
import (
"fmt"
"io"
"net/http"
"os"
)
func main() {
http.HandleFunc("/hello", doLogic)
http.ListenAndServe(":3070", nil)
}
func doLogic(w http.ResponseWriter, r *http.Request) {
r.ParseMultipartForm(32 << 20) // 32MB
sourceFile, handler, err := r.FormFile("myfile")
if err != nil {
fmt.Println(err)
return
}
defer sourceFile.Close()
fmt.Fprintf(w, "%v", handler.Header)
filePath := "/tmp2/" + handler.Filename
targetFile, err := os.OpenFile(filePath, os.O_WRONLY|os.O_CREATE, 0666)
if err != nil {
fmt.Println(err)
return
}
defer targetFile.Close()
io.Copy(targetFile, sourceFile)
rawContent := GetFileContent(filePath)
Encrypt(rawContent)
Decrypt("myfile.data")
}
fileUtil.go
package main
import (
"fmt"
"io/ioutil"
"os"
)
func GetFileContent(filePath string) []byte {
file, err := os.Open(filePath)
if err != nil {
panic(err)
}
defer file.Close()
content, _ := ioutil.ReadAll(file)
fmt.Println(string(content))
return []byte(string(content))
}
encrypt.go
package main
import (
"crypto/aes"
"crypto/cipher"
"crypto/rand"
"fmt"
"io"
"io/ioutil"
)
func Encrypt(text []byte) {
fmt.Println("Encryption Program v0.01")
// text := []byte("this is a message need to be encrypted")
key := []byte("thisismypassword")
// generate a new aes cipher using our 32 byte long key
c, err := aes.NewCipher(key)
// if there are any errors, handle them
if err != nil {
fmt.Println(err)
}
// gcm or Galois/Counter Mode, is a mode of operation
// for symmetric key cryptographic block ciphers
// - https://en.wikipedia.org/wiki/Galois/Counter_Mode
gcm, err := cipher.NewGCM(c)
// if any error generating new GCM
// handle them
if err != nil {
fmt.Println(err)
}
// creates a new byte array the size of the nonce
// which must be passed to Seal
nonce := make([]byte, gcm.NonceSize())
// populates our nonce with a cryptographically secure
// random sequence
if _, err = io.ReadFull(rand.Reader, nonce); err != nil {
fmt.Println(err)
}
// here we encrypt our text using the Seal function
// Seal encrypts and authenticates plaintext, authenticates the
// additional data and appends the result to dst, returning the updated
// slice. The nonce must be NonceSize() bytes long and unique for all
// time, for a given key.
fmt.Println(gcm.Seal(nonce, nonce, text, nil))
// the WriteFile method returns an error if unsuccessful
err = ioutil.WriteFile("myfile.data", gcm.Seal(nonce, nonce, text, nil), 0777)
// handle this error
if err != nil {
// print it out
fmt.Println(err)
}
}
decrypt.go
package main
import (
"crypto/aes"
"crypto/cipher"
"fmt"
"io/ioutil"
)
func Decrypt(filePath string) {
fmt.Println("Decryption Program v0.01")
key := []byte("thisismypassword")
ciphertext, err := ioutil.ReadFile("myfile.data")
// if our program was unable to read the file
// print out the reason why it can't
if err != nil {
fmt.Println(err)
}
c, err := aes.NewCipher(key)
if err != nil {
fmt.Println(err)
}
gcm, err := cipher.NewGCM(c)
if err != nil {
fmt.Println(err)
}
nonceSize := gcm.NonceSize()
if len(ciphertext) < nonceSize {
fmt.Println(err)
}
nonce, ciphertext := ciphertext[:nonceSize], ciphertext[nonceSize:]
plaintext, err := gcm.Open(nil, nonce, ciphertext, nil)
if err != nil {
fmt.Println(err)
}
fmt.Println(string(plaintext))
}
在这个例子中,使用了golang的 AES 包
使用 postman 上传文件之后,会保存一个临时文件到服务器上,然后使用AES对文件内容加密,最后我们使用了decrypt.go 对内容进行了解密
GPG 加密基础
对称加密
lizhe@ubuntu:~/works/gpg$ echo helloworld > hello.txt
lizhe@ubuntu:~/works/gpg$ cat hello.txt
helloworld
lizhe@ubuntu:~/works/gpg$ gpg -c hello.txt
lizhe@ubuntu:~/works/gpg$ cat hello.txt.gpg
/7lizhe@ubuntu:~/works/gpg$ ,��'�`�h)����>�[?�L斔(k*�M��v��)� ��&�[a���'��
lizhe@ubuntu:~/works/gpg$
lizhe@ubuntu:~/works/gpg$ gpg -d hello.txt.gpg
gpg: AES256 encrypted data
gpg: encrypted with 1 passphrase
helloworld
lizhe@ubuntu:~/works/gpg$
非对称加密
gpg --full-gen-key
对称加密只要知道密码,就可以在任意电脑上打开原来的文件
非对称加密一定要保管好秘钥文件
gpg --recipient lizhe --encrypt hello.txt
解密
gpg -d hello.txt.gpg
对于对称加密而言,只要知道密码,到哪里都可以打开,但是对于 RSA,一定要备份好私钥
列出私钥
gpg -K
列出公钥
gpg -k
导出私钥
gpg -a -o private-file.key --export-secret-keys keyId
导出公钥
gpg -a -o public-file.key --export keyId
把加密文件和私钥拷贝到另一台电脑上
直接解密会得到
我们来导入 私钥
gpg --import private-file.key
再试一次
删除
gpg –delete-keys 6C8A15CECD3DCC2741A7C590AB38BACE635A064C
gpg –delete-secret-keys 6C8A15CECD3DCC2741A7C590AB38BACE635A064C
使用密码
gpg –import –pinentry-mode loopback –batch –passphrase password private-file.key
gpg –import –pinentry-mode loopback –batch –passphrase-file password-file private-file.key
AES和RSA都很安全,至少在目前的计算机体系结构下,没有任何有效的攻击方式
RSA有一定的破绽,因为利用shro’s algorithm,量子计算机穷举计算质因子速度可以提高N个数量级,能够在有限的时间内破解RSA密钥。AES256至少目前并没有什么明显的漏洞.
AES作为对称加密技术,加密速度很快, 但是如何安全的分发密码,就成了问题
RSA的公钥可以随意分发,但是 加密大文件时,性能开销确实也是个问题
特殊情况下,如果需要加密 大文件,最安全的方式还是 RSA 加密 密码,然后使用这个密码 进行 AES 加密
也就是 RSA 分发密码 + AES 加密 的方式