03 kubernetes 的网络实现

Service 对象

为什么需要 service

每个 pod 都有自己的 ip 地址,但是在 Deployment 中,在同一时刻运行的 pod 集合可能与稍后运行该应用程序的 pod 集合不同。这导致了一个问题,如果一组 Pod(称为后端)为集群内的其他 pod (称为前端)提供功能,那么前端如何找出并跟踪要连接的 ip 地址,以便前端可以使用后端部分呢?

Service 概念

将运行在一组 pods 上的应用程序公开为网络服务的抽象方法。

使用 kubernetes 服务无需修改应用程序即可使用通用的服务发现机制。kubernetes 为 pods 提供自己的 ip 地址,并为一组 pod 提供相同的 DNS 名,并且可以在它们之间进行负载均衡。

定义 service

service 在 kubernetes 中是一个 REST 对象,和 pod 类似。像所有的 REST 对象一样,service 定义可以基于 POST 方式,请求 API server 创建新的实例。

创建一个 service

vim service.yaml
apiVersion: v1
kind: Service
metadata:
    # service 的名称,将来会被用作请求的域名来被集群的内部或外部对象使用
  name: my-service
spec:
    # 匹配 pod 
  selector:
    app: MyApp
  ports:
    - protocol: TCP
        # service 的端口
      port: 80
      # 容器的端口
      targetPort: 9376
kubectl create -f service.yaml

查看 service 信息

kubectl get svc

可以看到:

NAME         TYPE        CLUSTER-IP   EXTERNAL-IP   PORT(S)   AGE
kubernetes   ClusterIP   10.1.0.1     <none>        443/TCP   47h
my-service   ClusterIP   10.1.22.99   <none>        80/TCP    74s

用 service 暴露 pod 服务地址

创建一个 Deployment,包含两个 nginx pod ,具有一个容器端口80:

vim nginx-deplyment.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  name: my-nginx
spec:
    # 筛选
  selector:
  # 根据标签匹配
    matchLabels:
        # 标签
      run: my-nginx
  # pod 的数量
  replicas: 2
  # 模板
  template:
    metadata:
        # 标签
      labels:
        run: my-nginx
    spec:
        # 容器
      containers:
      - name: my-nginx
        image: registry.cn-beijing.aliyuncs.com/qingfeng666/nginx:latest
        ports:
        - containerPort: 80
kubectl create -f nginx-deplyment.yaml
kubectl get po

可以看到:

my-nginx-76d78c9795-5jzjg   1/1     Running            0          2m13s
my-nginx-76d78c9795-frcmq   1/1     Running            0          2m13s

查看 pod 的 ip 地址

kubectl get pods -l run=my-nginx -o yaml | grep podIP

上面的参数

  • -l 表示:label
  • -o 表示:output

可以看到:

podIP: 10.244.1.44
podIP: 10.244.2.5

不直接访问 pod ,通过 service 访问 pod。创建 service :

vim nginx-service.yaml
apiVersion: v1
kind: Service
metadata:
  name: my-nginx
  labels:
    run: my-nginx
spec:
  ports:
  - port: 80
    protocol: TCP
  selector:
    run: my-nginx
kubectl create -f nginx-service.yaml
kubectl get svc

查看 service 的描述信息

kubectl describe svc my-nginx

可以看到:

Name:              my-nginx
Namespace:         default
Labels:            run=my-nginx
Annotations:       <none>
Selector:          run=my-nginx
Type:              ClusterIP
IP:                10.1.12.12
Port:              <unset>  80/TCP
TargetPort:        80/TCP
Endpoints:         10.244.1.44:80,10.244.2.5:80
Session Affinity:  None
Events:            <none>
curl 10.1.12.12:80

可以看到:

Welcome to nginx!

集群内 pod 通信机制

Kubernetes 支持两种基本的服务发现模式:环境变量和 DNS

环境变量

当 pod 运行在 node 上,kubelet 会为每个活跃的 service 添加一组环境变量。它同时支持 Docker Links,简单的 {SVCNAME}_SERVICE_HOST{SVCNAME}_SERVICE_PORT 变量。这里 service 的名称需要大写,横线被转换成下划线。

举个例子,一个名称是 my-nginx 的 service 暴露了 TCP 端口 80,同时给它分配了 Cluster IP 地址 10.1.180.155 ,这个 service 生成了如下环境变量:

MY_NGINX_PORT_80_TCP_PORT=80
MY_NGINX_PORT_80_TCP_PROTO=tcp
MY_NGINX_PORT_80_TCP_ADDR=10.1.180.155

Service 要比 pod 先创建,才能把环境变量写入 pod

  1. 创建 service:kubectl create -f nginx-service.yaml

  2. 创建 Deployment:kubectl create -f nginx-deplyment.yaml

  3. 查看 pod :kubectl get po

  4. 进入 pod: kubectl exec -it my-nginx-76d78c9795-5jzjg sh

  5. 打印环境变量:printenv

# printenv |grep MY_NGINX
MY_NGINX_PORT=tcp://10.1.99.100:80
MY_NGINX_SERVICE_PORT=80
MY_NGINX_PORT_80_TCP_ADDR=10.1.99.100
MY_NGINX_PORT_80_TCP_PORT=80
MY_NGINX_PORT_80_TCP_PROTO=tcp
MY_NGINX_PORT_80_TCP=tcp://10.1.99.100:80
MY_NGINX_SERVICE_HOST=10.1.99.100

DNS

可以通过使用附加组件为 kubernetes 集群设置 DNS 服务

支持集群的 DNS 服务器(例如 CoreDNS)监视 kubetnetes API 中的新服务,并为每个服务创建一组 DNS 记录。如果在整个集群中都启用了 DNS ,则所有 pod 都应该能够通过其 DNS 名称自动解析服务。

举个例子, 如果在 kubernetes 命名空间 my-ns 中有一个名为 my-service 的新服务,则控制节点和 DNS 服务共同为 my-service.my-ns 创建 DNS 纪录。my-ns 命名空间中的 pod 应该能够通过对 my-service 进行名称查找来找到它。(my-service.my-ns 也可以)

其他命名空间中的 pod 必须将名称限定为 my-service.my-ns 。这些名称将解析为为服务分配的集群 ip 。

Service 创建 DNS 纪录

Kubernetes DNS 在集群上调度 DNS Pod 和服务,并配置 kubelet 以告知各个容器使用 DNS 服务的 IP 来解析 DNS 名称。

创建一个 service 和两个 pod:

vim dns.yaml

apiVersion: v1
kind: Service
metadata:
  name: default-subdomain
spec:
  selector:
    name: busybox
  clusterIP: None
  ports:
  - name: foo
    port: 1234
    targetPort: 1234
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox1
  labels:
    name: busybox
spec:
  hostname: busybox-1
  subdomain: default-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox
---
apiVersion: v1
kind: Pod
metadata:
  name: busybox2
  labels:
    name: busybox
spec:
  hostname: busybox-2
  subdomain: default-subdomain
  containers:
  - image: busybox:1.28
    command:
      - sleep
      - "3600"
    name: busybox

hostname 表示 pod 的主机名

subdomain 表示 pod 的子域名

kubectl create -f dns.yaml

可以看到:

service/default-subdomain created
pod/busybox1 created
pod/busybox2 created

查看 pod

kubectl get po

可以看到:

NAME                        READY   STATUS             RESTARTS   AGE
busybox1                    1/1     Running            0          2m14s
busybox2                    1/1     Running            0          2m14s

进入 pod 查看域名:

[root@node1 ~]# kubectl exec -it busybox2 sh
kubectl exec [POD] [COMMAND] is DEPRECATED and will be removed in a future version. Use kubectl exec [POD] -- [COMMAND] instead.
/ # hostname
busybox-2
/ # hostname -f
busybox-2.default-subdomain.default.svc.cluster.local
/ # exit
[root@node1 ~]#

从集群外部访问 service

从集群外部访问 service 的方法:

  1. ClusterIP

    仅仅使用一个集群内部的 IP 地址--这是默认值。选择这个值意味着这个服务你只想在集群内部被访问到。

  2. NodePort

    在集群内部 IP 的基础上,在集群的每一个节点的端口上开放这个服务。你可以在任意 NodePort 地址上访问到这个服务。

  3. LoadBalancer

    在使用一个集群内部地址和在 NodePort 上开放一个 service 的基础上,还可以向云提供者申请一个负载均衡器,将流量转发到已经以 NodePort 的形式开放的 service 上。

创建 nginx pod 和 NodePort 类型的 service

vim nodeport.yaml
apiVersion: apps/v1
kind: Deployment
metadata:
  labels:
    app: nginx
  name: nginx-deployment
spec:
  replicas: 1
  selector:
    matchLabels:
      app: nginx
  template:
    metadata:
      labels:
        app: nginx
    spec:
      containers:
      - image: nginx
        name: nginx
---
apiVersion: v1
kind: Service
metadata:
  labels:
    app: nginx
  name: nginx-deployment
spec:
  ports:
  - port: 80
    name: nginx-service80
    protocol: TCP
    targetPort: 80
    nodePort: 30001  
  selector:
    app: nginx
  type: NodePort

创建

kubectl create -f nodeport.yaml

可以看到

deployment.apps/nginx-deployment created
service/nginx-deployment created
kubectl get all

可以看到

NAME                                    READY   STATUS    RESTARTS   AGE
pod/nginx-deployment-6799fc88d8-lh9m5   1/1     Running   0          104s

NAME                       TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)        AGE
service/kubernetes         ClusterIP   10.1.0.1       <none>        443/TCP        6d5h
service/nginx-deployment   NodePort    10.1.238.159   <none>        80:30001/TCP   104s

通过浏览器访问:192.168.190.132:30001 (ip 是节点的 )

可以看到:

Welcome to nginx!

访问的流程:

节点 ip:30001 > service:80 > pod:80

Ingress

Ingress 是对集群中服务的外部访问进行管理的 API 对象,典型的访问方式是:HTTP

Ingress 公开了从集群外部到集群内部的 HTTP 和 HTTPS 路由,流量路由由 Ingress 资源上定义的规则控制

创建一个 Ingress 资源

vim minimal-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: minimal-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
  - http:
      paths:
      - path: /testpath
        pathType: Prefix
        backend:
          service:
            name: test
            port:
              number: 80
  1. 可选的 host 。上面的例子中没有指定 host,因此该规则适用于通过指定 IP 地址的所有入站 HTTP 通信。如果提供了 host ,则规则(rules)适用于该 host 。
  2. 路径列表 paths 。每个路径都有一个由 serviceName 和 servicePort 定义的关联后端。在负载均衡器将流量定向到引用的服务之前,主机和路径都必须匹配传入请求的内容。
  3. 后端 backend 。是 service 文档中所述的服务和端口名称的组合。与规则的 host 和 paths 匹配的 Ingress 的 HTTP (和 HTTPS)请求将发送到列出的 backend 。

安装 Nginx Ingress 控制器

vim ingress-nginx-controller.yaml
apiVersion: v1
kind: Namespace
metadata:
  name: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
 
---
 
kind: ConfigMap
apiVersion: v1
metadata:
  name: nginx-configuration
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
 
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: tcp-services
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
 
---
kind: ConfigMap
apiVersion: v1
metadata:
  name: udp-services
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
 
---
apiVersion: v1
kind: ServiceAccount
metadata:
  name: nginx-ingress-serviceaccount
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
 
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRole
metadata:
  name: nginx-ingress-clusterrole
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
rules:
  - apiGroups:
      - ""
    resources:
      - configmaps
      - endpoints
      - nodes
      - pods
      - secrets
    verbs:
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - nodes
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - services
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - ""
    resources:
      - events
    verbs:
      - create
      - patch
  - apiGroups:
      - "extensions"
      - "networking.k8s.io"
    resources:
      - ingresses
    verbs:
      - get
      - list
      - watch
  - apiGroups:
      - "extensions"
      - "networking.k8s.io"
    resources:
      - ingresses/status
    verbs:
      - update
 
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: Role
metadata:
  name: nginx-ingress-role
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
rules:
  - apiGroups:
      - ""
    resources:
      - configmaps
      - pods
      - secrets
      - namespaces
    verbs:
      - get
  - apiGroups:
      - ""
    resources:
      - configmaps
    resourceNames:
      - "ingress-controller-leader-nginx"
    verbs:
      - get
      - update
  - apiGroups:
      - ""
    resources:
      - configmaps
    verbs:
      - create
  - apiGroups:
      - ""
    resources:
      - endpoints
    verbs:
      - get
 
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: RoleBinding
metadata:
  name: nginx-ingress-role-nisa-binding
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: Role
  name: nginx-ingress-role
subjects:
  - kind: ServiceAccount
    name: nginx-ingress-serviceaccount
    namespace: ingress-nginx
 
---
apiVersion: rbac.authorization.k8s.io/v1beta1
kind: ClusterRoleBinding
metadata:
  name: nginx-ingress-clusterrole-nisa-binding
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
roleRef:
  apiGroup: rbac.authorization.k8s.io
  kind: ClusterRole
  name: nginx-ingress-clusterrole
subjects:
  - kind: ServiceAccount
    name: nginx-ingress-serviceaccount
    namespace: ingress-nginx
---
kind: Service
apiVersion: v1
metadata:
  name: ingress-nginx
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  clusterIP: 10.1.211.240
  externalTrafficPolicy: Cluster
  ports:
  - name: http
    nodePort: 31686
    port: 80
    protocol: TCP
    targetPort: http
  - name: https
    nodePort: 30036
    port: 443
    protocol: TCP
    targetPort: https
  selector:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
  sessionAffinity: None
  type: NodePort
---
 
apiVersion: apps/v1
kind: Deployment
metadata:
  name: nginx-ingress-controller
  namespace: ingress-nginx
  labels:
    app.kubernetes.io/name: ingress-nginx
    app.kubernetes.io/part-of: ingress-nginx
spec:
  replicas: 1
  selector:
    matchLabels:
      app.kubernetes.io/name: ingress-nginx
      app.kubernetes.io/part-of: ingress-nginx
  template:
    metadata:
      labels:
        app.kubernetes.io/name: ingress-nginx
        app.kubernetes.io/part-of: ingress-nginx
      annotations:
        prometheus.io/port: "10254"
        prometheus.io/scrape: "true"
    spec:
      terminationGracePeriodSeconds: 300
      serviceAccountName: nginx-ingress-serviceaccount
      containers:
        - name: nginx-ingress-controller
          image: registry.aliyuncs.com/google_containers/nginx-ingress-controller:0.26.1
          args:
            - /nginx-ingress-controller
            - --configmap=$(POD_NAMESPACE)/nginx-configuration
            - --tcp-services-configmap=$(POD_NAMESPACE)/tcp-services
            - --udp-services-configmap=$(POD_NAMESPACE)/udp-services
            - --publish-service=$(POD_NAMESPACE)/ingress-nginx
            - --annotations-prefix=nginx.ingress.kubernetes.io
          securityContext:
            allowPrivilegeEscalation: true
            capabilities:
              drop:
                - ALL
              add:
                - NET_BIND_SERVICE
            runAsUser: 33
          env:
            - name: POD_NAME
              valueFrom:
                fieldRef:
                  fieldPath: metadata.name
            - name: POD_NAMESPACE
              valueFrom:
                fieldRef:
                  fieldPath: metadata.namespace
          ports:
            - name: http
              containerPort: 80
            - name: https
              containerPort: 443
          livenessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            initialDelaySeconds: 10
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 10
          readinessProbe:
            failureThreshold: 3
            httpGet:
              path: /healthz
              port: 10254
              scheme: HTTP
            periodSeconds: 10
            successThreshold: 1
            timeoutSeconds: 10
          lifecycle:
            preStop:
              exec:
                command:
                  - /wait-shutdown

---

创建

kubectl apply -f ingress-nginx-controller.yaml

查看,指定 namespace

kubectl get all -n ingress-nginx

可以看到

NAME                                           READY   STATUS    RESTARTS   AGE
pod/nginx-ingress-controller-95598b695-4j5nw   1/1     Running   0          84s

NAME                    TYPE       CLUSTER-IP     EXTERNAL-IP   PORT(S)                      AGE
service/ingress-nginx   NodePort   10.1.211.240   <none>        80:31686/TCP,443:30036/TCP   84s

NAME                                       READY   UP-TO-DATE   AVAILABLE   AGE
deployment.apps/nginx-ingress-controller   1/1     1            1           84s

NAME                                                 DESIRED   CURRENT   READY   AGE
replicaset.apps/nginx-ingress-controller-95598b695   1         1         1       84s

检查部署版本

POD_NAMESPACE=ingress-nginx
POD_NAME=$(kubectl get pods -n $POD_NAMESPACE -l app.kubernetes.io/name=ingress-nginx --field-selector=status.phase=Running -o jsonpath='{.items[0].metadata.name}')

kubectl exec -it $POD_NAME -n $POD_NAMESPACE -- /nginx-ingress-controller --version

可以看到

NGINX Ingress controller
  Release:       0.26.1
  Build:         git-2de5a893a
  Repository:    https://github.com/kubernetes/ingress-nginx
  nginx version: openresty/1.15.8.2

Nginx Ingress 控制器实践

部署一个 hello world 服务

  1. 创建一个 Deployment

    kubectl create deployment web --image=registry.cn-beijing.aliyuncs.com/qingfeng666/hello-app:1.0
    

    可以看到:

    deployment.apps/web created
    
  2. 把 Deployment 暴露出来

    kubectl expose deployment web --type=NodePort --port=8080
    

    expose 这里会创建一个 NodePort 类型的 service ,端口是 8080

    可以看到:

    service/web exposed
    

    查看 service

    kubectl get svc
    

    可以看到:

    NAME               TYPE        CLUSTER-IP     EXTERNAL-IP   PORT(S)          AGE
    web                NodePort    10.1.163.170   <none>        8080:31886/TCP   93s
    
  3. 使用节点 ip 加端口 31886 访问

    curl node1:31886
    

    可以看到:

    Hello, world!
    Version: 1.0.0
    Hostname: web-6db77f5fdb-kh7ks
    

创建一个 Ingress 资源

vim example-ingress.yaml
apiVersion: networking.k8s.io/v1
kind: Ingress
metadata:
  name: example-ingress
  annotations:
    nginx.ingress.kubernetes.io/rewrite-target: /
spec:
  rules:
    - host: hello-world.info
      http:
        paths:
          - path: /
            pathType: Prefix
            backend:
              service:
                name: web
                port:
                  number: 8080

创建

kubectl apply -f example-ingress.yaml

可以看到:

ingress.networking.k8s.io/example-ingress created

查看 Ingress

kubectl get ing

可以看到

NAME              CLASS    HOSTS              ADDRESS        PORTS   AGE
example-ingress   <none>   hello-world.info   10.1.211.240   80      42s

添加域名

vim /etc/hosts

添加

10.1.211.240 hello-world.info

验证 Ingress 能够转发请求

curl hello-world.info

可以看到

Hello, world!
Version: 1.0.0
Hostname: web-6db77f5fdb-kh7ks
©著作权归作者所有,转载或内容合作请联系作者
  • 序言:七十年代末,一起剥皮案震惊了整个滨河市,随后出现的几起案子,更是在滨河造成了极大的恐慌,老刑警刘岩,带你破解...
    沈念sama阅读 225,151评论 6 523
  • 序言:滨河连续发生了三起死亡事件,死亡现场离奇诡异,居然都是意外死亡,警方通过查阅死者的电脑和手机,发现死者居然都...
    沈念sama阅读 96,465评论 3 405
  • 文/潘晓璐 我一进店门,熙熙楼的掌柜王于贵愁眉苦脸地迎上来,“玉大人,你说我怎么就摊上这事。” “怎么了?”我有些...
    开封第一讲书人阅读 172,429评论 0 368
  • 文/不坏的土叔 我叫张陵,是天一观的道长。 经常有香客问我,道长,这世上最难降的妖魔是什么? 我笑而不...
    开封第一讲书人阅读 61,147评论 1 301
  • 正文 为了忘掉前任,我火速办了婚礼,结果婚礼上,老公的妹妹穿的比我还像新娘。我一直安慰自己,他们只是感情好,可当我...
    茶点故事阅读 70,149评论 6 400
  • 文/花漫 我一把揭开白布。 她就那样静静地躺着,像睡着了一般。 火红的嫁衣衬着肌肤如雪。 梳的纹丝不乱的头发上,一...
    开封第一讲书人阅读 53,614评论 1 315
  • 那天,我揣着相机与录音,去河边找鬼。 笑死,一个胖子当着我的面吹牛,可吹牛的内容都是我干的。 我是一名探鬼主播,决...
    沈念sama阅读 41,965评论 3 429
  • 文/苍兰香墨 我猛地睁开眼,长吁一口气:“原来是场噩梦啊……” “哼!你这毒妇竟也来了?” 一声冷哼从身侧响起,我...
    开封第一讲书人阅读 40,950评论 0 279
  • 序言:老挝万荣一对情侣失踪,失踪者是张志新(化名)和其女友刘颖,没想到半个月后,有当地人在树林里发现了一具尸体,经...
    沈念sama阅读 47,486评论 1 324
  • 正文 独居荒郊野岭守林人离奇死亡,尸身上长有42处带血的脓包…… 初始之章·张勋 以下内容为张勋视角 年9月15日...
    茶点故事阅读 39,524评论 3 347
  • 正文 我和宋清朗相恋三年,在试婚纱的时候发现自己被绿了。 大学时的朋友给我发了我未婚夫和他白月光在一起吃饭的照片。...
    茶点故事阅读 41,640评论 1 355
  • 序言:一个原本活蹦乱跳的男人离奇死亡,死状恐怖,灵堂内的尸体忽然破棺而出,到底是诈尸还是另有隐情,我是刑警宁泽,带...
    沈念sama阅读 37,228评论 5 351
  • 正文 年R本政府宣布,位于F岛的核电站,受9级特大地震影响,放射性物质发生泄漏。R本人自食恶果不足惜,却给世界环境...
    茶点故事阅读 42,976评论 3 340
  • 文/蒙蒙 一、第九天 我趴在偏房一处隐蔽的房顶上张望。 院中可真热闹,春花似锦、人声如沸。这庄子的主人今日做“春日...
    开封第一讲书人阅读 33,407评论 0 25
  • 文/苍兰香墨 我抬头看了看天上的太阳。三九已至,却和暖如春,着一层夹袄步出监牢的瞬间,已是汗流浃背。 一阵脚步声响...
    开封第一讲书人阅读 34,552评论 1 277
  • 我被黑心中介骗来泰国打工, 没想到刚下飞机就差点儿被人妖公主榨干…… 1. 我叫王不留,地道东北人。 一个月前我还...
    沈念sama阅读 50,215评论 3 381
  • 正文 我出身青楼,却偏偏与公主长得像,于是被迫代替她去往敌国和亲。 传闻我的和亲对象是个残疾皇子,可洞房花烛夜当晚...
    茶点故事阅读 46,714评论 2 366

推荐阅读更多精彩内容

  • 今天感恩节哎,感谢一直在我身边的亲朋好友。感恩相遇!感恩不离不弃。 中午开了第一次的党会,身份的转变要...
    迷月闪星情阅读 10,580评论 0 11
  • 彩排完,天已黑
    刘凯书法阅读 4,245评论 1 3
  • 表情是什么,我认为表情就是表现出来的情绪。表情可以传达很多信息。高兴了当然就笑了,难过就哭了。两者是相互影响密不可...
    Persistenc_6aea阅读 125,526评论 2 7