• Kubernetes之(十三)ConfigMap和Secret


    Kubernetes之(十三)ConfigMap和Secret

    简介
    ConfigMap和Secret是kubernetes系统上两种特殊类型的存储卷,ConfigMao对象用于为容器中的应用提供配置数据以定制程序行为,不过年敏感的配置信息,例如密钥,证书等通常由Secret对象来进行配置,它们将相应的配置信息保存于对象中,而后在Pod资源上以存储卷的形式将其挂载并获取相关配置,以实现配置与镜像文件的解耦。

    传统的实践过程中们需要对一个应用进行配置,只需要修改其配置文件通常有以下几种方式:

    • 启动容器时,通过命令传递参数 command args自定义参数
    • 把配置文件写入镜像内
    • 通过环境变量的方式传递配置数据
      • cloud native的程序,一般通过环境变量加载配置
      • 通过enteypoint脚本预处理变量为配置文件中的配置信息
    • 挂载Docker卷传送配置文件

    而在Kubernetes系统之中也存在这样的组件,就是特殊的存储卷类型。其并不是提供pod存储空间,而是给管理员或用户提供从集群外部向Pod内部的应用注入配置信息的方式。这两种特殊类型的存储卷:configMap和secret

    ConfigMap:主要用于向Pod注入非敏感数据,使用时,用户将数据直接存储在ConfigMap对象当中,然后Pod通过使用ConfigMap卷进行引用,实现容器的配置文件集中定义和管理。
    Secret:用于向Pod传递敏感信息,比如密码,私钥,证书文件等,这些信息如果在容器中定义容易泄露,Secret资源可以让用户将这些信息存储在急群众,然后通过Pod进行挂载,实现敏感数据和系统解耦的效果。

    ConfigMap

    configmap是让配置文件从镜像中解耦,让镜像的可移植性和可复制性。许多应用程序会从配置文件、命令行参数或环境变量中读取配置信息。这些配置信息需要与docker image解耦,ConfigMap API给我们提供了向容器中注入配置信息的机制,ConfigMap可以被用来保存单个属性,也可以用来保存整个配置文件或者JSON二进制对象。

    ConfigMap API资源用来保存key-value pair配置数据,这个数据可以在pods里使用,或者被用来为像controller一样的系统组件存储配置数据。虽然ConfigMap跟Secrets类似,但是ConfigMap更方便的处理不含敏感信息的字符串。 注意:ConfigMaps不是属性配置文件的替代品。ConfigMaps只是作为多个properties文件的引用。可以把它理解为Linux系统中的/etc目录,专门用来存储配置文件的目录。下面举个例子,使用ConfigMap配置来创建Kuberntes Volumes,ConfigMap中的每个data项都会成为一个新文件。

    [root@master ~]# kubectl explain cm.
    KIND:     ConfigMap
    VERSION:  v1
    
    FIELDS:
       apiVersion   <string>
    
       data <map[string]string>
    
       kind <string>
    
       metadata     <Object>
    

    ConfigMap创建方式

    1、 通过 --from-literal:

    [root@master configmap]# kubectl create configmap nginx-config --from-literal=nginx_port=8080 --from-literal=server_name=myapp.white.com
    configmap/nginx-config created
    [root@master configmap]# kubectl get cm
    NAME           DATA   AGE
    nginx-config   2      4s
    
    [root@master configmap]# kubectl describe cm nginx-config 
    Name:         nginx-config
    Namespace:    default
    Labels:       <none>
    Annotations:  <none>
    
    Data
    ====
    nginx_port:
    ----
    8080
    server_name:
    ----
    myapp.white.com
    Events:  <none>
    

    2、通过 --from-file:

    [root@master configmap]# vim www.conf
    server {
            server_name myapp.white.com;
            listen 80;
            root /data/web/html/;
    }
    
    
    [root@master configmap]# kubectl create configmap nginx-www --from-file=./www.conf 
    configmap/nginx-www created
    [root@master configmap]# kubectl get cm
    NAME           DATA   AGE
    nginx-config   2      3m5s
    nginx-www      1      3s
    [root@master configmap]# kubectl get cm nginx-www -o yaml
    apiVersion: v1
    data:
      www.conf: "server {
    	server_name myapp.white.com;
    	listen 80;
    	root /data/web/html/;
    }
    "
    kind: ConfigMap
    metadata:
      creationTimestamp: "2019-04-03T02:25:57Z"
      name: nginx-www
      namespace: default
      resourceVersion: "207536"
      selfLink: /api/v1/namespaces/default/configmaps/nginx-www
      uid: d0171cb8-55b7-11e9-80a7-000c295ec349
    

    将cm注入到pod内

    [root@master configmap]# vim pod-configmap.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-cm-1
      namespace: default
      labels:
        app: myapp
        tier: frontend
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1
        ports:
        - name: http
          containerPort: 80
        env:
        - name: NGINX_SERVER_PORT
          valueFrom:
            configMapKeyRef:
              name: nginx-config
              key: nginx_port
        - name: NGINX_SERVER_NAME
          valueFrom:
            configMapKeyRef:
              name: nginx-config
              key: server_name
    
    [root@master configmap]# kubectl get pods
    NAME                                 READY   STATUS    RESTARTS   AGE
    myapp-backend-pod-6b56d98b6b-2dh5h   1/1     Running   0          25h
    myapp-backend-pod-6b56d98b6b-hwzws   1/1     Running   0          25h
    myapp-backend-pod-6b56d98b6b-ztwn2   1/1     Running   0          25h
    pod-cm-1                             1/1     Running   0          19s
    pod-hostpath-vol                     1/1     Running   0          20h
    pod-pvc-vol                          1/1     Running   0          17h
    tomcat-deploy-5f554cd88d-7gzc7       1/1     Running   0          24h
    tomcat-deploy-5f554cd88d-c42t6       1/1     Running   0          24h
    tomcat-deploy-5f554cd88d-qhc4j       1/1     Running   0          24h
    
    

    进入容器查看:

    [root@master configmap]# kubectl exec -it pod-cm-1  -- /bin/sh
    / # printenv
    MYAPP_SVC_PORT_80_TCP_ADDR=10.98.57.156
    KUBERNETES_SERVICE_PORT=443
    KUBERNETES_PORT=tcp://10.96.0.1:443
    MYAPP_SERVICE_PORT_HTTP=80
    TOMCAT_PORT_8080_TCP=tcp://10.107.88.118:8080
    TOMCAT_PORT_8009_TCP=tcp://10.107.88.118:8009
    MYAPP_SVC_PORT_80_TCP_PORT=80
    HOSTNAME=pod-cm-1
    SHLVL=1
    MYAPP_SVC_PORT_80_TCP_PROTO=tcp
    HOME=/root
    MYAPP_SERVICE_HOST=10.100.41.152
    NGINX_SERVER_PORT=8080   #已经被调用
    NGINX_SERVER_NAME=myapp.white.com   #已经被调用
    MYAPP_SVC_PORT_80_TCP=tcp://10.98.57.156:80
    MYAPP_PORT=tcp://10.100.41.152:80
    MYAPP_SERVICE_PORT=80
    TERM=xterm
    NGINX_VERSION=1.12.2
    KUBERNETES_PORT_443_TCP_ADDR=10.96.0.1
    MYAPP_PORT_80_TCP_ADDR=10.100.41.152
    PATH=/usr/local/sbin:/usr/local/bin:/usr/sbin:/usr/bin:/sbin:/bin
    TOMCAT_SERVICE_PORT_HTTP=8080
    KUBERNETES_PORT_443_TCP_PORT=443
    KUBERNETES_PORT_443_TCP_PROTO=tcp
    MYAPP_PORT_80_TCP_PORT=80
    MYAPP_PORT_80_TCP_PROTO=tcp
    MYAPP_SVC_SERVICE_HOST=10.98.57.156
    TOMCAT_PORT_8080_TCP_ADDR=10.107.88.118
    TOMCAT_SERVICE_HOST=10.107.88.118
    TOMCAT_PORT_8009_TCP_ADDR=10.107.88.118
    KUBERNETES_SERVICE_PORT_HTTPS=443
    KUBERNETES_PORT_443_TCP=tcp://10.96.0.1:443
    TOMCAT_PORT_8080_TCP_PORT=8080
    PWD=/
    KUBERNETES_SERVICE_HOST=10.96.0.1
    MYAPP_PORT_80_TCP=tcp://10.100.41.152:80
    TOMCAT_PORT_8080_TCP_PROTO=tcp
    TOMCAT_PORT_8009_TCP_PORT=8009
    MYAPP_SVC_SERVICE_PORT=80
    MYAPP_SVC_PORT=tcp://10.98.57.156:80
    TOMCAT_PORT_8009_TCP_PROTO=tcp
    TOMCAT_PORT=tcp://10.107.88.118:8080
    TOMCAT_SERVICE_PORT=8080
    TOMCAT_SERVICE_PORT_AJP=8009
    

    如果此时使用kubectl edit cm来修改configmap 此时在容器内查看环境变量不会被修改,因为该env是在pod创建的时候获取的,不会被修改

    存储卷方式挂载configmap:

    [root@master configmap]# vim pod-configmap-2.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-cm-2
      namespace: default
      labels:
        app: myapp
        tier: frontend
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1
        ports:
        - name: http
          containerPort: 80
        volumeMounts:
        - name: nginxconf
          mountPath: /etc/nginx/config.d/
          readOnly: true
      volumes:
      - name: nginxconf
        configMap:
          name: nginx-config
    
    
    [root@master configmap]# kubectl apply -f pod-configmap-2.yaml 
    pod/pod-cm-2 created
    

    查看并进入Pod

    [root@master configmap]# kubectl get pods
    NAME                                 READY   STATUS    RESTARTS   AGE
    myapp-backend-pod-6b56d98b6b-2dh5h   1/1     Running   0          29h
    myapp-backend-pod-6b56d98b6b-hwzws   1/1     Running   0          29h
    myapp-backend-pod-6b56d98b6b-ztwn2   1/1     Running   0          29h
    pod-cm-2                             1/1     Running   0          53s
    pod-hostpath-vol                     1/1     Running   0          23h
    pod-pvc-vol                          1/1     Running   0          20h
    tomcat-deploy-5f554cd88d-7gzc7       1/1     Running   0          27h
    tomcat-deploy-5f554cd88d-c42t6       1/1     Running   0          27h
    tomcat-deploy-5f554cd88d-qhc4j       1/1     Running   0          27h
    
    [root@master configmap]# kubectl exec -it pod-cm-2 -- /bin/sh
    / # cd /etc/nginx/config.d/
    /etc/nginx/config.d # ls
    nginx_port   server_name
    /etc/nginx/config.d # cat nginx_port
    8080
    /etc/nginx/config.d # cat server_name
    myapp.white.com/etc/nginx/config.d 
    
    

    此时使用kubectl edit修改端口,进入容器进行查看

    [root@master ~]# kubectl edit cm nginx-config 
    
    # Please edit the object below. Lines beginning with a '#' will be ignored,
    # and an empty file will abort the edit. If an error occurs while saving this file will be
    # reopened with the relevant failures.
    #
    apiVersion: v1
    data:
      nginx_port: "11111"
      server_name: myapp.white.com
    kind: ConfigMap
    metadata:
      creationTimestamp: "2019-04-03T02:22:55Z"
      name: nginx-config
      namespace: default
      resourceVersion: "207257"
      selfLink: /api/v1/namespaces/default/configmaps/nginx-config
      uid: 63e91046-55b7-11e9-80a7-000c295ec349
    

    进入容器查看(需要等待一小会,有一定延迟时间)

    /etc/nginx/config.d # cat nginx_port 
    11111
    /etc/nginx/config.d #
    

    使用nginx-www配置nginx

    nginx-www是使用文件创建的cm,文件名是键名,内容是值。

    [root@master configmap]# vim pod-configmap-3.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-cm-3
      namespace: default
      labels:
        app: myapp
        tier: frontend
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1
        ports:
        - name: http
          containerPort: 80
        volumeMounts:
        - name: nginxconf
          mountPath: /etc/nginx/conf.d/
          readOnly: true
      volumes:
      - name: nginxconf
        configMap:
          name: nginx-www
    

    创建并查看

    [root@master configmap]# kubectl apply -f pod-configmap-3.yaml
    pod/pod-cm-3 created
    [root@master configmap]# kubectl get pods
    NAME                                 READY   STATUS    RESTARTS   AGE
    myapp-backend-pod-6b56d98b6b-2dh5h   1/1     Running   0          29h
    myapp-backend-pod-6b56d98b6b-hwzws   1/1     Running   0          29h
    myapp-backend-pod-6b56d98b6b-ztwn2   1/1     Running   0          29h
    pod-cm-3                             1/1     Running   0          5s
    pod-hostpath-vol                     1/1     Running   0          23h
    pod-pvc-vol                          1/1     Running   0          20h
    tomcat-deploy-5f554cd88d-7gzc7       1/1     Running   0          28h
    tomcat-deploy-5f554cd88d-c42t6       1/1     Running   0          28h
    tomcat-deploy-5f554cd88d-qhc4j       1/1     Running   0          28h
    
    

    进入容器查看

    [root@master configmap]# kubectl exec -it pod-cm-3 -- /bin/sh 
    / # cd /etc/nginx/conf.d/
    /etc/nginx/conf.d # ls
    www.conf
    /etc/nginx/conf.d # cat www.conf 
    server {
            server_name myapp.white.com;
            listen 80;
            root /data/web/html/;
    }
    
    查看nginx加载的内容
    /etc/nginx/conf.d # nginx -T
    ... ...
    # configuration file /etc/nginx/conf.d/www.conf:
    server {
            server_name myapp.white.com;
            listen 80;
            root /data/web/html/;
    }
    

    此时使用kubectl edit修改cm内的内容,容器内部会被修改,但是nginx 需要重载才能生效,如果需要这样配置,需要手动或配置脚本进行nginx -s reload。

    Secret

    Secret对象存储数据的方式是以键值方式存储数据,在Pod资源进行调用Secret的方式是通过环境变量或者存储卷的方式进行访问数据,解决了密码、token、密钥等敏感数据的配置问题,而不需要把这些敏感数据暴露到镜像或者Pod Spec中。另外,Secret对象的数据存储和打印格式为Base64编码的字符串,因此用户在创建Secret对象时,也需要提供该类型的编码格式的数据。在容器中以环境变量或存储卷的方式访问时,会自动解码为明文格式。需要注意的是,如果是在Master节点上,Secret对象以非加密的格式存储在etcd中,所以需要对etcd的管理和权限进行严格控制。
    Secret有4种类型:

    • Service Account :用来访问Kubernetes API,由Kubernetes自动创建,并且会自动挂载到Pod的/run/secrets/kubernetes.io/serviceaccount目录中;
    • Opaque :base64编码格式的Secret,用来存储密码、密钥、信息、证书等,类型标识符为generic;
    • kubernetes.io/dockerconfigjson :用来存储私有docker registry的认证信息,类型标识为docker-registry。
    • kubernetes.io/tls:用于为SSL通信模式存储证书和私钥文件,命令式创建类型标识为tls。

    创建 Secret

    使用--from-literal

    [root@master configmap]# kubectl create secret generic mysql-root-password --from-literal=password=MyP@ss123
    secret/mysql-root-password created
    [root@master configmap]# kubectl get secret
    NAME                    TYPE                                  DATA   AGE
    default-token-dqd2f     kubernetes.io/service-account-token   3      6d22h
    mysql-root-password     Opaque                                1      4s
    tomcat-ingress-secret   kubernetes.io/tls                     2      28h
    

    查看

    [root@master configmap]# kubectl describe secret mysql-root-password
    Name:         mysql-root-password
    Namespace:    default
    Labels:       <none>
    Annotations:  <none>
    
    Type:  Opaque
    
    Data
    ====
    password:  9 bytes
    

    查看可知只有9个字节,没有显示内容。可以转换成YAML格式文件查看Base64加密后的password

    [root@master configmap]# kubectl describe secret mysql-root-password
    Name:         mysql-root-password
    Namespace:    default
    Labels:       <none>
    Annotations:  <none>
    
    Type:  Opaque
    
    Data
    ====
    password:  9 bytes
    [root@master configmap]# kubectl get secret mysql-root-password -o yaml
    apiVersion: v1
    data:
      password: TXlQQHNzMTIz
    kind: Secret
    metadata:
      creationTimestamp: "2019-04-03T06:46:00Z"
      name: mysql-root-password
      namespace: default
      resourceVersion: "226351"
      selfLink: /api/v1/namespaces/default/secrets/mysql-root-password
      uid: 247548a3-55dc-11e9-80a7-000c295ec349
    type: Opaque
    

    解码

    [root@master configmap]# echo TXlQQHNzMTIz|base64 -d
    MyP@ss123
    

    使用--from-file

    [root@master configmap]# echo -n admin > ./username
    [root@master configmap]#  echo -n 123456 > ./password
    [root@master configmap]# kubectl create secret generic mysecret --from-file=./username --from-file=./password 
    secret/mysecret created
    [root@master configmap]# kubectl get secret
    NAME                    TYPE                                  DATA   AGE
    default-token-dqd2f     kubernetes.io/service-account-token   3      6d22h
    mysecret                Opaque                                2      4s
    mysql-root-password     Opaque                                1      6m6s
    tomcat-ingress-secret   kubernetes.io/tls                     2      28h
    

    通过 --from-env-file

    文件 env.txt 中每行 Key=Value 对应一个信息条目。

    [root@master configmap]# vim pod-secret-1.yaml 
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-secret-1
      namespace: default
      labels:
        app: myapp
        tier: frontend
    spec:
      containers:
      - name: myapp
        image: ikubernetes/myapp:v1
        ports:
        - name: http
          containerPort: 80
        env:
        - name: MYSQL_ROOT_PASSWORD
          valueFrom:
            secretKeyRef:
              name: mysql-root-password
              key: password
    
    
    [root@master configmap]# kubectl apply -f pod-secret-1.yaml 
    pod/pod-secret-1 created
    
    

    进入Pod查看

    [root@master configmap]# kubectl exec -it pod-secret-1 -- /bin/sh
    / # printenv|grep MYSQL_ROOT_PASSWORD
    MYSQL_ROOT_PASSWORD=MyP@ss123
    

    在Pod内已经是转换后的密码,任何人都可以看到,不够安全。

    使用挂在存储卷方式

    [root@master configmap]# kubectl get secret
    NAME                    TYPE                                  DATA   AGE
    default-token-dqd2f     kubernetes.io/service-account-token   3      6d22h
    mysecret                Opaque                                2      11m
    mysql-root-password     Opaque                                1      17m
    tomcat-ingress-secret   kubernetes.io/tls                     2      28h
    
    [root@master configmap]# vim pod-secret-demo.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-secret-vol
    spec:
      containers:
      - name: pod-secret-vol
        image: busybox
        args:
        - /bin/sh
        - -c
        - sleep 10;touch /tmp/healthy;sleep 36000
        volumeMounts:
        - name: test
          mountPath: "/etc/test"
          readOnly: true
      volumes:
      - name: test
        secret:
          secretName: mysecret
    
    [root@master configmap]# kubectl apply -f pod-secret-demo.yaml 
    pod/pod-secret-vol created
    
    [root@master configmap]# kubectl get pods
    NAME                                 READY   STATUS    RESTARTS   AGE
    myapp-backend-pod-6b56d98b6b-2dh5h   1/1     Running   0          30h
    myapp-backend-pod-6b56d98b6b-hwzws   1/1     Running   0          30h
    myapp-backend-pod-6b56d98b6b-ztwn2   1/1     Running   0          30h
    pod-hostpath-vol                     1/1     Running   0          24h
    pod-pvc-vol                          1/1     Running   0          21h
    pod-secret-1                         1/1     Running   0          9m
    pod-secret-vol                       1/1     Running   0          7s
    tomcat-deploy-5f554cd88d-7gzc7       1/1     Running   0          29h
    tomcat-deploy-5f554cd88d-c42t6       1/1     Running   0          29h
    tomcat-deploy-5f554cd88d-qhc4j       1/1     Running   0          29h
    

    进入Pod查看

    [root@master configmap]# kubectl exec -it pod-secret-vol -- /bin/sh
    / # cd /etc/test/
    /etc/test # ls
    password  username
    /etc/test # cat username
    admin
    /etc/test # cat password
    123456
    

    可以看到,Kubernetes 会在指定的路径 /etc/test 下为每条敏感数据创建一个文件,文件名就是数据条目的 Key,这里是 /etc/test/username 和 /etc/test/password,Value 则以明文存放在文件中。
    也可以自定义存放数据的文件名,比如将配置文件改为:

    [root@master configmap]# vim  pod-secret-damo-1.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-secret-vol-1
    spec:
      containers:
      - name: pod-secret-vol-1
        image: busybox
        args:
        - /bin/sh
        - -c
        - sleep 10;touch /tmp/healthy;sleep 36000
        volumeMounts:
        - name: test
          mountPath: "/etc/test"
          readOnly: true
      volumes:
      - name: test
        secret:
          secretName: mysecret
          items:
          - key: username
            path: my-secret/my-username
          - key: password
            path: my-secret/my-password
    
    
    

    这时数据将分别存放在 /etc/test/my-secret/my-username 和 /etc/test/my-secret/my-password 中。

    [root@master configmap]# kubectl exec -it pod-secret-vol-1 -- /bin/sh
    / # cd /etc/test/
    /etc/test # ls
    my-secret
    /etc/test # cd my-secret/
    /etc/test/..2019_04_03_07_12_02.941048565/my-secret # ls
    my-password  my-username
    /etc/test/..2019_04_03_07_12_02.941048565/my-secret # cat  my-username
    admin
    /etc/test/..2019_04_03_07_12_02.941048565/my-secret # cat my-password
    123456
    

    以 Volume 方式使用的 Secret 支持动态更新:Secret 更新后,容器中的数据也会更新。
    将 password 更新为 abcdef,base64 编码为 YWJjZGVm

    [root@master configmap]#  vim secret.yaml
    apiVersion: v1
    kind: Secret
    metadata:
      name: mysecret
    data:
      username: YWRtaW4=   #echo -n  admin|base64
      password: YWJjZGVm   #echo -n   abcdef|base64
    
    
    [root@master configmap]# kubectl exec -it pod-secret-vol-1 -- /bin/sh
    /etc # cd test/my-secret/
    /etc/test/..2019_04_03_07_18_09.073816450/my-secret # ls
    my-password  my-username
    /etc/test/..2019_04_03_07_18_09.073816450/my-secret # cat my-password 
    abcdef
    

    通过 Volume 使用 Secret,容器必须从文件读取数据,会稍显麻烦,Kubernetes 还支持通过环境变量使用 Secret。

    [root@master configmap]# vim pod-secret-env-demo.yaml
    apiVersion: v1
    kind: Pod
    metadata:
      name: pod-secret-env
    spec:
      containers:
      - name: pod-secret-env
        image: busybox
        args:
        - /bin/sh
        - -c
        - sleep 10;touch /tmp/healthy;sleep 36000
        env:
          - name: SECRET_USERNAME
            valueFrom:
              secretKeyRef:
                name: mysecret
                key: username
          - name: SECRET_PASSWORD
            valueFrom:
              secretKeyRef:
                name: mysecret
                key: password
    
    
    [root@master configmap]# kubectl apply -f pod-secret-env-demo.yaml
    pod/pod-secret-env created
    
    [root@master configmap]# kubectl exec -it pod-secret-env sh
    / #  echo $SECRET_USERNAME
    admin
    / # echo $SECRET_PASSWORD
    abcdef
    

    总结:
    通过环境变量 SECRET_USERNAME 和 SECRET_PASSWORD 成功读取到 Secret 的数据。
    需要注意的是,环境变量读取 Secret 很方便,但不支持Secret 动态更新。

    参考资料

    https://www.cnblogs.com/linuxk
    马永亮. Kubernetes进阶实战 (云计算与虚拟化技术丛书)
    Kubernetes-handbook-jimmysong-20181218

  • 相关阅读:
    【CF1027C】Minimum Value Rectangle(贪心,数学)
    【CF1027B】Numbers on the Chessboard(数学)
    【CF1027A】Palindromic Twist(模拟)
    【CF1023C】Bracket Subsequence(模拟)
    【CF1023B】Pair of Toys(解方程)
    【CF1023A】Single Wildcard Pattern Matching(模拟)
    【CF1020C】Elections(贪心)
    【CF1020B】Badge(模拟)
    【CF1020A】New Building for SIS(签到)
    【CF1017D】The Wu(状压前缀和)
  • 原文地址:https://www.cnblogs.com/wlbl/p/10694353.html
Copyright © 2020-2023  润新知