[{"data":1,"prerenderedAt":-1},["ShallowReactive",2],{"$fPELyMcIcCF5muRXflzCWsXVMWWihg7xToybk2otYQRc":3},{"data":4,"related":91},{"id":5,"title":6,"slug":7,"excerpt":8,"cover":9,"category":11,"author":14,"track":20,"level":23,"reading_minutes":26,"is_featured":27,"published_at":28,"updated_at":28,"body":29,"toc":30,"tags":60,"faqs":76,"related_services":86,"comments_enabled":89,"meta_title":19,"meta_description":90,"canonical_url":19,"noindex":27,"is_live":89},5,"قفل state ترافورم گیر کرده؛ آزادسازی امن بعد از کیل شدن apply","terraform-state-lock-stuck-force-unlock","وقتی پایپلاین CI وسط اجرای apply کشته می‌شود، قفل state ترافورم در جدول DynamoDB باقی می‌ماند و اجرای بعدی با خطای Error acquiring the state lock متوقف می‌شود. در این مقاله علت ریشه‌ای، روش آزادسازی امن و راه‌های پیشگیری را بررسی می‌کنیم.",{"url":10,"alt":6},"https:\u002F\u002Fapi.cloudicap.com\u002Fmedia\u002Fposts\u002Fe26789f7-eced-4abb-b6a9-a91adf9b7500.webp",{"name":12,"slug":13},"زیرساخت ابری و IaC","cloud-infrastructure",{"name":15,"slug":16,"role":17,"bio":18,"linkedin_url":19,"avatar_url":19},"تحریریه کلودیکپ","cloudicap-editorial","تیم محتوای کلودیکپ","تیم فنی و محتوای کلودیکپ؛ مهندسانی که هر روز با DevOps، Kubernetes و زیرساخت ابری کار می‌کنند و تجربه‌هایشان را اینجا می‌نویسند.",null,{"value":21,"label":22},"engineers","مهندسان",{"value":24,"label":25},"intermediate","متوسط",6,false,"2026-10-08T09:49:26+00:00","\u003Ch2 id=\"شرح-مسئله\">شرح مسئله\u003C\u002Fh2>\n\u003Cp>ساعت ۹ صبح است و می‌خواهی تغییرات تیم زیرساخت را روی محیط production اعمال کنی. \u003Ccode>terraform apply\u003C\u002Fcode> را می‌زنی و به‌جای خروجی معمول، این را می‌بینی:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">$ terraform apply\nAcquiring state lock. This may take a few moments...\n\nError: Error acquiring the state lock\n\nError message: ConditionalCheckFailedException: The conditional request failed\nLock Info:\n  ID:        a1b2c3d4-5678-90ab-cdef-1234567890ab\n  Path:      prod-network\u002Fterraform.tfstate\n  Operation: OperationTypeApply\n  Who:       runner@gitlab-ci-runner-7f9c\n  Version:   1.6.2\n  Created:   2024-03-11 09:14:22.123456789 +0000 UTC\n  Info:\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>نکته مهم در لاگ، فیلدهای \u003Ccode>ID\u003C\u002Fcode>، \u003Ccode>Who\u003C\u002Fcode> و \u003Ccode>Created\u003C\u002Fcode> است که به زمان حال مربوط نیستند. یعنی قفل قبلاً توسط یک اجرای دیگر گرفته شده و آزاد نشده است. اگر همان لحظه در ترمینال دیگری یا در رابط کاربری GitLab\u002FGitHub Actions نگاه کنی، می‌بینی هیچ اجرای فعالی در جریان نیست. قفل «روح» شده است.\u003C\u002Fp>\n\n\u003Ch2 id=\"علت-ریشه‌ای\">علت ریشه‌ای\u003C\u002Fh2>\n\u003Cp>بک‌اند S3 در ترافورم به‌تنهایی قفل ندارد. برای همین در کنارش از یک جدول DynamoDB با کلید \u003Ccode>LockID\u003C\u002Fcode> استفاده می‌شود. جریان کار این‌طور است:\u003C\u002Fp>\n\u003Col>\n\u003Cli>پیش از هر عملیات، ترافورم تلاش می‌کند یک آیتم با کلید مسیر state را در جدول قفل درج کند.\u003C\u002Fli>\n\u003Cli>درج با شرط \u003Ccode>attribute_not_exists(LockID)\u003C\u002Fcode> انجام می‌شود؛ اگر آیتم از قبل موجود باشد، خطای \u003Ccode>ConditionalCheckFailedException\u003C\u002Fcode> برمی‌گردد و ترافورم با همان پیام بالا متوقف می‌شود.\u003C\u002Fli>\n\u003Cli>در پایان موفقیت‌آمیز اجرا، ترافورم آیتم را حذف می‌کند.\u003C\u002Fli>\n\u003C\u002Fol>\n\u003Cp>مشکل جایی است که فرایند ترافورم به شکل تمیز خاتمه پیدا نکند. ترافورم سیگنال \u003Ccode>SIGINT\u003C\u002Fcode> (Ctrl+C) را مدیریت می‌کند و قفل را آزاد می‌کند، اما روی \u003Ccode>SIGKILL\u003C\u002Fcode> هیچ فرصتی برای این کار ندارد. سناریوهای رایج:\u003C\u002Fp>\n\u003Cul>\n\u003Cli>تمام‌شدن timeout جاب CI و کشته‌شدن runner.\u003C\u002Fli>\n\u003Cli>OOM Kill شدن پاد یا کانتینر runner.\u003C\u002Fli>\n\u003Cli>قطع‌ومنت شبکه هنگام نوشتن state در S3.\u003C\u002Fli>\n\u003Cli>پاک‌شدن ناگهانی runner اسپات در میان اجرا.\u003C\u002Fli>\n\u003Cli>بسته‌شدن ناگهانی لپ‌تاپ یا VPN در حال اجرا.\u003C\u002Fli>\n\u003C\u002Ful>\n\u003Cp>در همه این حالت‌ها آیتم قفل در جدول DynamoDB باقی می‌ماند و ترافورم دیگر نمی‌تواند وارد شود.\u003C\u002Fp>\n\n\u003Ch2 id=\"راه‌حل-گام‌به‌گام\">راه‌حل گام‌به‌گام\u003C\u002Fh2>\n\n\u003Ch3 id=\"قدم-۱-مطمئن-شو-هیچ-اجرایی-در-حال-انجام-نیست\">قدم ۱: مطمئن شو هیچ اجرایی در حال انجام نیست\u003C\u002Fh3>\n\u003Cp>پیش از هر کاری، بررسی کن که کسی در حال apply نیست. آزادسازی نسنجیده می‌تواند دو اجرای همزمان را روی یک state راه بیندازد و state را خراب کند.\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\"># دیدن آیتم قفل فعلی\naws dynamodb get-item \\\n  --table-name terraform-locks \\\n  --key '{\"LockID\": {\"S\": \"my-terraform-state\u002Fprod-network\u002Fterraform.tfstate\"}}'\n\n# دیدن همه قفل‌های فعلی جدول\naws dynamodb scan \\\n  --table-name terraform-locks \\\n  --projection-expression \"LockID, Info\"\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>در خروجی به فیلد \u003Ccode>Info\u003C\u002Fcode> نگاه کن؛ همان چیزی است که در پیام خطا دیدی. اگر \u003Ccode>Created\u003C\u002Fcode> مربوط به ساعت‌ها یا روزها قبل است و در CI هم جاب فعالی در جریان نیست، قفل متروک است. بهتر است تاریخچه جاب‌های اخیر را هم چک کنی؛ معمولاً آخرین اجرای نیمه‌کاره، همان قفل‌گیرنده است.\u003C\u002Fp>\n\n\u003Ch3 id=\"قدم-۲-آزادسازی-با-force-unlock\">قدم ۲: آزادسازی با force-unlock\u003C\u002Fh3>\n\u003Cp>ترافورم برای همین حالت یک دستور دارد که همان \u003Ccode>ID\u003C\u002Fcode> را می‌گیرد:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">terraform force-unlock a1b2c3d4-5678-90ab-cdef-1234567890ab\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>اگر می‌خواهی بدون تأییدیه تعاملی اجرا شود:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">terraform force-unlock -force a1b2c3d4-5678-90ab-cdef-1234567890ab\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cblockquote>مهم: این دستور آیتم قفل را حذف می‌کند، ولی هیچ کاری با خود state نمی‌کند. اگر اجرای نیمه‌کاره بخشی از تغییرات را در S3 نوشته باشد، آن نوشته سر جایش می‌ماند و باید جداگانه بررسی شود.\u003C\u002Fblockquote>\n\n\u003Ch3 id=\"قدم-۳-اگر-force-unlock-جواب-نداد\">قدم ۳: اگر force-unlock جواب نداد\u003C\u002Fh3>\n\u003Cp>گاهی دسترسی به بک‌اند از ماشین محلی درست پیکربندی نشده و ترافورم نمی‌تواند قفل را ببیند. در این حالت مستقیم آیتم را حذف کن:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">aws dynamodb delete-item \\\n  --table-name terraform-locks \\\n  --key '{\"LockID\": {\"S\": \"my-terraform-state\u002Fprod-network\u002Fterraform.tfstate\"}}'\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>سپس با \u003Ccode>aws dynamodb get-item\u003C\u002Fcode> تأیید کن که آیتم حذف شده است.\u003C\u002Fp>\n\n\u003Ch3 id=\"قدم-۴-بررسی-سازگاری-state\">قدم ۴: بررسی سازگاری state\u003C\u002Fh3>\n\u003Cp>پس از آزادسازی، اول یک plan بگیر:\u003C\u002Fp>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">terraform init -reconfigure\nterraform plan -out=tfplan\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>اگر در plan تعداد زیادی resource به‌شکل create\u002Fupdate غیرمنتظره ظاهر شد که قبلاً اعمال شده بودند، احتمالاً اجرای نیمه‌کاره بخشی از state را نوشته و بخشی را نه. در این حالت به‌جای apply کورکورانه، ابتدا با تیم بررسی کن و در صورت نیاز از نسخه‌های پشتیبان state استفاده کن.\u003C\u002Fp>\n\n\u003Ch2 id=\"روش-بررسی-اینکه-مشکل-واقعا-حل-شده-است\">روش بررسی اینکه مشکل واقعاً حل شده است\u003C\u002Fh2>\n\u003Cp>پس از آزادسازی، سه معیار را چک کن:\u003C\u002Fp>\n\u003Col>\n\u003Cli>\u003Ccode>terraform plan\u003C\u002Fcode> بدون خطای قفل اجرا شود و عملیات را کامل کند.\u003C\u002Fli>\n\u003Cli>در جدول DynamoDB هیچ آیتمی با آن \u003Ccode>LockID\u003C\u002Fcode> باقی نمانده باشد.\u003C\u002Fli>\n\u003Cli>خروجی plan دقیقاً انتظار تیم را منعکس کند، نه مجموعه‌ای از createهای عجیب.\u003C\u002Fli>\n\u003C\u002Fol>\n\n\u003Cpre>\u003Ccode class=\"language-bash\">aws dynamodb scan --table-name terraform-locks --select COUNT --query \"Count\"\n\u003C\u002Fcode>\u003C\u002Fpre>\n\n\u003Cp>اگر عدد صفر برگشت و هیچ اجرای فعالی در CI وجود ندارد، قفل تمیز است. دستور \u003Ccode>terraform plan\u003C\u002Fcode> را یک بار دیگر اجرا کن و ببین که بدون هیچ تأخیری قفل را می‌گیرد.\u003C\u002Fp>\n\n\u003Ch2 id=\"پیشگیری-و-بهترین-روش‌ها\">پیشگیری و بهترین روش‌ها\u003C\u002Fh2>\n\u003Cul>\n\u003Cli>\u003Cstrong>زمان انتظار قفل را تعیین کن:\u003C\u002Fstrong> در CI از \u003Ccode>-lock-timeout\u003C\u002Fcode> استفاده کن تا اجرای جدید چند دقیقه صبر کند و بعد خطا بدهد، نه اینکه فوراً بمیرد:\n\u003Cpre>\u003Ccode class=\"language-bash\">terraform apply -lock-timeout=10m\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Fli>\n\u003Cli>\u003Cstrong>برای عملیات فقط-خواندنی قفل نگیر:\u003C\u002Fstrong> برای plan در PRها می‌توانی از \u003Ccode>-lock=false\u003C\u002Fcode> استفاده کنی، ولی هرگز برای \u003Ccode>apply\u003C\u002Fcode>:\n\u003Cpre>\u003Ccode class=\"language-bash\">terraform plan -lock=false\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Fli>\n\u003Cli>\u003Cstrong>timeout جاب CI را واقع‌گرایانه تنظیم کن:\u003C\u002Fstrong> اگر apply معمولاً ۱۵ دقیقه طول می‌کشد، timeout را ۱۰ دقیقه نگذار. مقدار را بر اساس P95 اندازه‌گیری‌های واقعی انتخاب کن.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>از SIGKILL پرهیز کن:\u003C\u002Fstrong> در اسکریپت CI از \u003Ccode>timeout --signal=INT\u003C\u002Fcode> استفاده کن تا ترافورم فرصت آزادسازی قفل داشته باشد:\n\u003Cpre>\u003Ccode class=\"language-bash\">timeout --signal=INT 30m terraform apply -auto-approve\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Fli>\n\u003Cli>\u003Cstrong>جدول قفل را مانیتور کن:\u003C\u002Fstrong> روی جدول DynamoDB یک متریک CloudWatch برای تعداد آیتم و سن آن بساز و اگر قفلی بیشتر از حد معمول باقی ماند هشدار بگیر.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>state را قفل و نسخه‌بندی کن:\u003C\u002Fstrong> در بک‌اند S3 گزینه \u003Ccode>encrypt\u003C\u002Fcode> و bucket versioning را فعال نگه دار تا بازگردانی آسان باشد.\u003C\u002Fli>\n\u003Cli>\u003Cstrong>پیکربندی مرجع بک‌اند:\u003C\u002Fstrong>\n\u003Cpre>\u003Ccode class=\"language-hcl\">terraform {\n  backend \"s3\" {\n    bucket         = \"my-terraform-state\"\n    key            = \"prod-network\u002Fterraform.tfstate\"\n    region         = \"us-east-1\"\n    dynamodb_table = \"terraform-locks\"\n    encrypt        = true\n  }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Fli>\n\u003Cli>\u003Cstrong>مهاجرت به قفل بومی S3:\u003C\u002Fstrong> از ترافورم 1.10 به بعد، بک‌اند S3 خودش می‌تواند قفل را با \u003Ccode>use_lockfile = true\u003C\u002Fcode> روی خود state مدیریت کند و دیگر نیازی به DynamoDB نداشته باشی:\n\u003Cpre>\u003Ccode class=\"language-hcl\">terraform {\n  backend \"s3\" {\n    bucket       = \"my-terraform-state\"\n    key          = \"prod-network\u002Fterraform.tfstate\"\n    region       = \"us-east-1\"\n    encrypt      = true\n    use_lockfile = true\n  }\n}\n\u003C\u002Fcode>\u003C\u002Fpre>\nدر این حالت دیگر لازم نیست جدول DynamoDB بسازی. اگر همچنان از DynamoDB استفاده می‌کنی، جدول را با همین اسکیمای ساده ایجاد کن:\n\u003Cpre>\u003Ccode class=\"language-bash\">aws dynamodb create-table \\\n  --table-name terraform-locks \\\n  --attribute-definitions AttributeName=LockID,AttributeType=S \\\n  --key-schema AttributeName=LockID,KeyType=HASH \\\n  --billing-mode PAY_PER_REQUEST\n\u003C\u002Fcode>\u003C\u002Fpre>\n\u003C\u002Fli>\n\u003C\u002Ful>\n\n\u003Cp>جمع‌بندی: قفل state یک مکانیزم محافظ است، نه دشمن. هر بار که مجبور می‌شوی \u003Ccode>force-unlock\u003C\u002Fcode> بزنی، یک فرایند در CI یا اسکریپت محلی وجود دارد که به‌درستی خاتمه پیدا نمی‌کند. با تنظیم timeouts، مدیریت سیگنال‌ها و \u003Ca href=\"\u002Fservices\u002Fmonitoring\">مانیتورینگ\u003C\u002Fa> جدول قفل، تعداد این اتفاق‌ها به‌سرعت به صفر نزدیک می‌شود.\u003C\u002Fp>",[31,35,38,41,45,48,51,54,57],{"id":32,"text":33,"level":34},"شرح-مسئله","شرح مسئله",2,{"id":36,"text":37,"level":34},"علت-ریشه‌ای","علت ریشه‌ای",{"id":39,"text":40,"level":34},"راه‌حل-گام‌به‌گام","راه‌حل گام‌به‌گام",{"id":42,"text":43,"level":44},"قدم-۱-مطمئن-شو-هیچ-اجرایی-در-حال-انجام-نیست","قدم ۱: مطمئن شو هیچ اجرایی در حال انجام نیست",3,{"id":46,"text":47,"level":44},"قدم-۲-آزادسازی-با-force-unlock","قدم ۲: آزادسازی با force-unlock",{"id":49,"text":50,"level":44},"قدم-۳-اگر-force-unlock-جواب-نداد","قدم ۳: اگر force-unlock جواب نداد",{"id":52,"text":53,"level":44},"قدم-۴-بررسی-سازگاری-state","قدم ۴: بررسی سازگاری state",{"id":55,"text":56,"level":34},"روش-بررسی-اینکه-مشکل-واقعا-حل-شده-است","روش بررسی اینکه مشکل واقعاً حل شده است",{"id":58,"text":59,"level":34},"پیشگیری-و-بهترین-روش‌ها","پیشگیری و بهترین روش‌ها",[61,64,67,70,73],{"name":62,"slug":63},"CI\u002FCD","cicd",{"name":65,"slug":66},"Terraform","terraform",{"name":68,"slug":69},"State Lock","state-lock",{"name":71,"slug":72},"DynamoDB","dynamodb",{"name":74,"slug":75},"AWS","aws",[77,80,83],{"question":78,"answer":79},"آیا force-unlock کردن قفل ریسک دارد؟","بله. اگر واقعاً یک اجرای دیگر در حال apply باشد و تو قفل را به‌زور آزاد کنی، دو ترافورم همزمان روی یک state کار می‌کنند و احتمال خرابی state بالا می‌رود. پس همیشه اول با بررسی جدول DynamoDB و تاریخچه جاب‌های CI مطمئن شو که اجرای فعالی وجود ندارد، بعد آزاد کن.",{"question":81,"answer":82},"چطور بفهمم قفل واقعاً متروک است و کسی در حال apply نیست؟","فیلد Created در پیام خطا و در آیتم DynamoDB را ببین. اگر زمان آن مربوط به ساعت‌ها پیش است، در CI هیچ جاب فعالی در حال اجرا نیست و در لاگ‌های runner اثری از فرایند ترافورم نیست، قفل متروک است.",{"question":84,"answer":85},"آیا می‌توانم به‌جای DynamoDB از قفل بومی S3 استفاده کنم؟","بله. از ترافورم 1.10 به بعد بک‌اند S3 گزینه use_lockfile را پشتیبانی می‌کند و قفل روی خود فایل state در S3 نگه داشته می‌شود. در این حالت دیگر نیازی به ساخت و نگهداری جدول DynamoDB نداری، ولی مهاجرت نیاز به دقت دارد و باید پس از تغییر، یک plan سالم بگیر.",[87,13,88],"infrastructure-as-code","cloud-migration",true,"خطای Error acquiring the state lock در ترافورم بعد از کشته‌شدن apply در CI: علت، آزادسازی امن قفل با force-unlock و روش‌های پیشگیری.",[92,106,119],{"id":93,"title":94,"slug":95,"excerpt":96,"cover":97,"category":99,"author":101,"track":102,"level":103,"reading_minutes":104,"is_featured":27,"published_at":105,"updated_at":105},4,"حلقه بی‌پایان Jira Automation: وقتی قانون خودش را صدا می‌زند","jira-automation-infinite-loop-fix","اگر trigger و action یک قانون Jira Automation روی یک رویداد بیفتند، قانون خودش را دوباره اجرا می‌کند و issue با صدها کامنت تکراری پر می‌شود. این مقاله علت، پاک‌سازی و راه‌حل پایدار را نشان می‌دهد.",{"url":98,"alt":94},"https:\u002F\u002Fapi.cloudicap.com\u002Fmedia\u002Fposts\u002F696958ce-e1fa-458e-b7df-1f1a99fe6a5f.webp",{"name":100,"slug":63},"CI\u002FCD و اتوماسیون",{"name":15,"slug":16,"role":17,"avatar_url":19},{"value":21,"label":22},{"value":24,"label":25},7,"2026-10-07T09:28:33+00:00",{"id":44,"title":107,"slug":108,"excerpt":109,"cover":110,"category":112,"author":115,"track":116,"level":117,"reading_minutes":104,"is_featured":27,"published_at":118,"updated_at":118},"قطع شدن WebSocket در HAProxy هر ۵۰ ثانیه؛ علت و راه‌حل","haproxy-websocket-timeout-50s","اگر WebSocket یا SSE پشت HAProxy هر ۵۰ ثانیه قطع می‌شود، مقصر تایماوت‌های پیش‌فرض فایل نمونه است. در این مقاله علت ریشه‌ای و پیکربندی درست timeout tunnel را بررسی می‌کنیم.",{"url":111,"alt":107},"https:\u002F\u002Fapi.cloudicap.com\u002Fmedia\u002Fposts\u002F26b583b4-d805-45d6-a265-a94d73b5a21d.webp",{"name":113,"slug":114},"Linux و مدیریت سرور","linux",{"name":15,"slug":16,"role":17,"avatar_url":19},{"value":21,"label":22},{"value":24,"label":25},"2026-10-07T07:57:29+00:00",{"id":34,"title":120,"slug":121,"excerpt":122,"cover":123,"category":125,"author":128,"track":129,"level":130,"reading_minutes":26,"is_featured":27,"published_at":131,"updated_at":131},"پرشدن دیسک PostgreSQL با WAL اسلات replication غیرفعال","postgresql-replication-slot-wal-disk-full","یک replication slot غیرفعال می‌تواند pg_wal را تا پر شدن کامل دیسک نگه دارد و دیتابیس را عملاً از کار بیندازد. در این مقاله ریشه‌یابی، رفع و پیشگیری از آن را گام‌به‌گام بررسی می‌کنیم.",{"url":124,"alt":120},"https:\u002F\u002Fapi.cloudicap.com\u002Fmedia\u002Fposts\u002F88c82e31-0162-4db1-835f-5c86fba77647.webp",{"name":126,"slug":127},"مانیتورینگ و پایداری","monitoring",{"name":15,"slug":16,"role":17,"avatar_url":19},{"value":21,"label":22},{"value":24,"label":25},"2026-10-06T17:39:51+00:00"]